最近以太坊啟動了“大都會”硬分叉,很重要的一個功能就是整合了ZCash的零知識證明技術zkSNARK。我們一起來看一下zkSNARK這個拗口的技術到底是什么鬼。
零知識證明
要了解zkSNARK,必須先理解什么是零知識證明。
關于零知識證明,概念并不難理解,我們以一個老掉牙的故事作為例子。
阿里巴巴被強盜抓住,為了保命,他需要向強盜證明自己擁有打開石門的密碼,同時又不能把密碼告訴強盜。他想出一個解決辦法,先讓強盜離開自己一箭之地,距離足夠遠讓強盜無法聽到口令,足夠近讓阿里巴巴無法在強盜的弓箭下逃生。阿里巴巴就在這個距離下向強盜展示了石門的打開和關閉。
這個整個過程就是零知識證明,證明者能夠在不向驗證者提供任何有用信息(石門的口令)的情況下,使驗證者相信某個論斷(阿里巴巴知道打開石門的方法)是正確的。
當然,現實生活中類似的應用有很多,大家可以參考阿里巴巴的零知識證明或者零知識證明。
在計算機世界里面,零知識的應用場景就更多了,例如我們常用非對稱加密來做身份認證,驗證方只要使用公鑰解出自己提供的隨機數,即可證明被認證方的身份,不需要其提供自己的私鑰。
以上例子都是針對特定場景的特定方法,比如說石門不是通過口令控制而是通過實物鑰匙控制,這個方法就不可用了,是否有一個通用方法去認證任何事件呢?
zkSNARK
zkSNARK是zero-knowledge succint non-interactive arguments of knowledge的簡稱,全稱里面每個單詞都有特定的含義:
Zero knowledge:零知識證明,見前文。
Succinctness:證據信息較短,方便驗證
Non-interactivity:幾乎沒有交互,證明者基本上只要提供一個字符串義工驗證。對于區塊鏈來說,這一點至關重要,意味著可以把該消息放在鏈上公開驗證。
Arguments:證明過程是計算完好(computationally soundness)的,證明者無法在合理的時間內造出偽證(破解)。跟計算完好對應的是理論完好(perfect soundness),密碼學里面一般都是要求計算完好。
of knowledge:對于一個證明者來說,在不知曉特定證明 (witness) 的前提下,構建一個有效的零知識證據是不可能的。
接下來,我們一步一步解釋這個zkSNARK到底是怎么實現的。
同態隱藏
說到zkSNARK,不能不提的一個概念就是同態隱藏,說它是zkSNARK的核心技術一點都不為過。
滿足下面三個條件的函數E(x),我們稱之為加法同態。
1.對于大部分的x,在給定的E(x)通常很難求解出x.
2.不同輸入將會得到不同輸出 - 因此如果x≠y,,則E(x)≠E(y).
3.如果某人知道了E(x)和E(y),,則他可以生成在算數運算式中的x和y.。比如,他們可以使用E(x)和E(y).來計算E(x+y)。
同理我們可以定義乘法同態甚至是全同態。
我們常用的的非對稱加密方式RSA和ECC都支持加法同態,計算和證明證明需要比較多的公式運算,有時間另外開一篇文章講解。
跟RSA和ECC一樣,注意這里的E(x)計算是在有限域里面進行,這個域下文稱為Fp。
有了同態隱藏這個利器以后,我們就可以實現一定程度的零知識證明了。
A擁有x和y兩個秘密的數字,需要向B證明這兩個數字的和是7,只需要執行下面三個步驟:
1.A計算E(x),E(y),并發送給B
2.因為函數E(x)滿足加法同態,B可以通過E(x),E(y)計算E(x+y)
3.B獨立計算E(7),并驗證E(x+y)=E(7)
多項式盲驗證
利用加法同態的特性,我們可以簡單的把零知識證明推廣到多項式中。
假定A知道一個最高d次的多項式P,而B想要知道對應某個s的E(P(s))
我們希望在驗證的過程中,A只知道P,不知道s,B只知道s,不知道P,可以通過下面方式實現:
1.對s的每個指數,B計算E(1),E(s),...,E(sd),并發送給A
2.A知道多項式的所有系數,可以利用同態特性計算P(s),并回送給B
KCA以及完整的多項式盲驗證
上一章提供的多項式盲驗證方式有一個致命的問題,就是B根本沒法驗證A是真正利用多項式P(s)去計算結果,也就是說無法證明A真正知道這個多項式P(X)。我們繼續完善一下上面的驗證。
我們先定義一個概念:α對是指滿足b=α*a的一對值(a,b)。注意這里的乘法其實是橢圓曲線(ECC)上的乘法,橢圓曲線上的運算符合兩個特性:一是當α值很大的情況下,很難通過a和b倒推出α,二是加法和乘法滿足可交換群的特性,也就是說加法和乘法交換律在橢圓曲線上也是成立的。橢圓曲線的運算很復雜,本文暫不詳述,大家只要記住橢圓函數的乘法滿足同態隱藏的特性,即可完成下面的證明。
我們利用α對的特性,構建一個稱為KCA(Knowledge of Coefficient Test and Assumption)的過程
1.B隨機選擇一個α生成α對(a,b),α自己保存,(a,b)發送給A
2.A選擇γ,生成(a′,b′)=(γ?a,γ?b),把(a′,b′)回傳給B。利用交換律,可以證明(a′,b′)也是一個α對,b′=γ?b=γα?a=α(γ?a)=α?a′
3.B校驗(a′,b′),證實是α對,就可以斷言A知道γ
這個證明可以推廣到多個α對的場景,稱為d-KCA
1.B發送一系列的α對給A
2.A使用(a′,b′)=(c1?a1+c2?a2,c1?b1+c2?b2)生成新的α對
3.B驗證通過,可以斷言A知道c數組
這個KCA咋看似乎沒有什么用,但正好可以補足了之前多項式盲驗證 的缺陷,一個完整的多項式盲驗證過程如下
0.因為橢圓曲線的乘法符合同態隱藏的特性,A和B可以共同選擇x?g作為E(x)
1.B計算g,s?g,…,sd?g和α?g,αs?g,…,αsd?g并發送給A,實際上過程同上一章的第一步,只是把E(x)替代成乘法,增加了αs相應的多項式結果
2.A計算a=P(s)?g,b=αP(s)?g并回傳
3.a值即為B所需校驗的E(P(s))結果,同時KCA保證了a值必然是通過多項式生成
好了,到這里喘口氣,回顧一下我們現在到底做到了些什么。
通過加法同態,我們可以實現加法隱藏,讓B在不知道x和y的情況下,校驗x+y的值。進一步,通過多項式盲驗證,我們可以在不暴露多項式P(X)的情況下,讓B校驗任意給定s對應的P(s)。
接下來坐好扶穩,我們要從多項式推廣到任意計算的盲驗證了。
任意計算轉換到多項式證明
直接上例子,假定A需要向B證明他知道c1,c2,c3,使(c1?c2)?(c1+c3)=7,按照慣例,c1,c2,c3需要對B保密。
我們要做的第一步就是把計算“拍平”,通過基本的運算符把原計算畫成這樣的“計算門電路”。
當然我們也可以用程序員比較熟悉的方式來表達
S1=C1*C2
S2=C1+C3
S3=S1*S2
通過增加中間變量,我們把復雜的計算拍平,使用最簡單的門電路表達。新的門電路跟原計算是等價的。
我們要做的第二步就是把每一個門電路表示為等價的向量點積形式,這個過程成為R1CS(rank-1 constraint system)。
對每個門電路,我們定義一組向量(a,b,c),使得s . a * s . b - s . c = 0。其中s代表全部輸入的向量,也就是[C1,C2,C3,S1,S2,S3],為了讓加法門也能用同樣的方式表達,我們增加一個虛擬的變量成為one,s向量變成[one,C1,C2,C3,S1,S2,S3]。
對應到第一個門
a=[0,1,0,0,0,0,0]
b=[0,0,1,0,0,0,0]
c=[0,0,0,0,1,0,0]
把s,a,b和c代入s . a * s . b - s . c = 0,得到C1*C2-S1=0,即這個向量表達跟第一個門是完全等價的。
同理我們可以計算第二個門
a=[1,0,0,0,0,0,0]
b=[0,1,0,1,0,0,0]
c=[0,0,0,0,0,1,0]
第三個門
a=[0,0,0,0,1,0,0]
b=[0,0,0,0,0,1,0]
c=[0,0,0,0,0,0,1]
好了,到這里,我們把一個計算式拍平成為門電路,接著又通過R1CS把門電路“編碼”成向量的表達方式。
接下來是最重要的一步,把向量表達式表示為多項式,從而把向量的驗證轉化為多項式的驗證,這個過程稱為QAP(Quadratic Arithmetic Programs)。
具體辦法是,在Fp上面選定任意三個不同的值,例如我們選定1,2,3,尋找一組多項式
使得多項式在x取值1,2,3的時候a,b,c數組的取值分別對應到前述三個門電路的向量。
問題轉化為通過已知解倒推多項式定義,這部分可以使用拉格朗日插值完成,本文不再詳述。這個過程中需要對向量的每個取值做拉格朗日插值,對于復雜問題,這個向量會非常龐大,計算過程會很復雜,這里可以利用快速傅里葉變換進行優化。
到這里,我們把原來的三個向量組表示成為一個用x表示的數組a(x),b(x),c(x)。
取多項式P(x)=s . a(x) * s . b(x) - s . c(x),根據我們原來的定義,在x取值為1,2或3的時候,P(x)=0。根據多項式特性,P(a)=0等價于P可以被(x-a)整除,P(x)一定能被(x-1)(x-2)(x-3)整除,也就是說存在H(X),使P(x)=T(x)*H(x),其中T(x)=(x-1)(x-2)(x-3)。
注意QAP這個過程把原來三個點的取值轉化成為一個多項式,相當于中間插入了很多沒有意義的值,這些值的取值與原公式是無關的。也就是說多項式的驗證與原計算的驗證本質并不等價,但驗證了多項式也就驗證了元計算。
好了,最終我們把原算式的證明轉化成為多項式的證明,只要證明P(x)=T(x)*H(x),即可驗證原算式。
匹諾曹協議
通過QAP,我們已經把計算式的證明轉化為多項式的證明,現在萬事具備,只欠東風,就差一個完整的驗證流程了。
為了簡化下文描述,我們定義s . a(x)為L(x),s . b(x)為R(x),s . c(x)為O(x),那么我們需要證明的等式就改寫成L(x)*R(x)-O(x)=T(x)*H(x)。L,R和O的最高階數是d,所以這個等式的最高階數是2d,我們知道,兩個不等價的多項式交點數量最多只有2d個,2d相較于有限域的元素個數p來說很小的情況下,我們可以采用采樣的方式驗證多項式相等,A隨意選擇多項式P(x)被校驗通過的概率只有2d/p。隨機采樣校驗的過程如下:
1.A按照上一章方法選擇多項式L,R,O,H
2.B選擇隨機點s,計算E(T(s))
3.A計算E(L(s)),E(R(s)),E(O(s)),E(H(s))? (根據B發過來的E(s),E(s2),...)
4.B檢驗E(L(s)*R(s)-O(s))=E(H(s)*T(s))
這個證明過程還有四個問題需要解決:
1.保證L,R,O從同一組參數s生成
這個證明過程存在一個缺陷,正如按照我們的定義L(x)=s . a(x),R(x)=s . b(x),O(x)=s . c(x),這里隱含了一個限定條件是L,R和O必須是由同一個向量s生成,證明中忽略了這一點,也就是說A可以通過選擇不符合這個限定條件的多項式來作弊。解決辦法仍然是KCA,只不過這次的KCA要復雜一些。
先定義兩個公式:
這個公式的含義是要把L,R,O的指數錯開,如果L,R,O真是從同一組s=[s1,....sm]生成的話,必然有
換句話說,只要A能給出F和Fi的線性組合,即可證明L,R,O符合限定條件。這個限定條件的問題就轉化為一個d-KCA的問題了。
1.B選擇隱秘的α,計算E(α*Fi)并發送給A
2.A計算E(αF)回傳給B
3.B根據本文公式自行計算E(F)并校驗α對
2.防止暴力破解
在現在的流程里面,A需要把E(L(s)),E(R(s)),E(O(s)),根據同態隱藏的特性,根據這些值無法倒推原多項式。但是如果需要驗證的問題,解不多的情況下,B還是可以通過窮舉的方式暴力破解原問題,得到A的原始數據。例如我們已知A有兩個正整數,要求盲驗證這兩個正整數的乘積是12,那么B完全可以窮舉乘積是12的所有正整數組合,正向執行驗證過程,與E(L(s)),E(R(s))和E(O(s))比對即可知道正確的答案是什么。
當然,我們也有解決辦法。解決思路就是在生成L,R,O的時候引入隨機偏置
因為
令
新的組合
任然可以通過多項式的校驗,而因為B不知道隨機數,也無法通過暴力破解的方式知曉原始參數。
3.乘法同態
匹諾曹協議的最后一步,B需要檢驗E(L(s)*R(s)-O(s))=E(H(s)*T(s)),而事實上,我們之前只提到E(x)滿足加法同態,B是無法通過E(H(s))計算出E(H(s)*T(s))的。
解決辦法需要回歸到我們的數學工具上,我們需要用到橢圓曲線配對的特性,這里說來話長,本文只給出結論。通過橢圓曲線配對,我們可以得到一個弱化版的乘法同態。
定義E1(x):=x?g,E2(x):=x?h,E(x):=x?g,因為三個函數都是橢圓曲線,自然分別都符合加法同態,同時橢圓曲線配對特性可以保證我們能通過E1(x),E2(y)計算E(xy)。
4.減少交互
最后一個問題也是最關鍵的一個問題是,匹諾曹協議中需要A和B之間做很多的消息交互,而在區塊鏈中,我們想要做到的是“公開認證”。最理想的情況就是只要A把證據作為一個字符串放置到鏈上,任何人都能驗證結論。
可惜的是,實際上這種嚴格意義上的零交互證明已經被證明不能滿足所有的證明場景。我們退而求其次,采用了一種稱為CRS(COMMON REFERENCE STRING)的方式。原理很簡單,實際上就是把隨機數α和s內置于“系統”中。
所以終極版的zkSNARK過程就是:
0.配置α和s,以之計算 (E1(1),E1(s),…,E1(sd),E2(α),E2(αs),…,E2(αsd))E2(α),并公示
1.A使用公示參數計算驗證多項式
2.B校驗多項式,乘法同態部分利用橢圓曲線配對的特性完成,形如E(αx)=Tate(E1(x),E2(α))
當然CRS有一個極其嚴重的問題就是,“系統”內建的隨機參數非常重要,知道這個秘密參數的人就擁有超級管理員的權限,可以任意制造偽幣,這在一個去中心化的系統中幾乎是不可接受的。
事實上,ZCash的系統參數采用了一種影視劇中經常出現的橋段去“保護”這個不應該也不需要由任何人掌握的配置數據。選擇世界各地六個可信任的人,每人生成密鑰一部分,六個人的密碼拼接在一起生成公示的數據后,再分別銷毀掉各自手上的密鑰。除非六人合謀作弊,否則沒有人擁有超級管理員的權限。
參考文獻
ZCash7篇,有社區翻譯版,但還是推薦看原汁原味的? ? ?https://z.cash/blog/snark-explain.html
Vitalik3篇,小天才作者我就不介紹了,這三篇介紹得也是很透徹
https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649
https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627
https://medium.com/@VitalikButerin/zk-snarks-under-the-hood-b33151a013f6
一篇不錯的中文介紹材料?http://news.btc123.com/news/detail?id=8125
libsnark開源庫地址?https://github.com/scipr-lab/libsnark