零知識證明系列(一):zkSNARK

買賣虛擬貨幣
前言我們學數學時都知道計算題和證明題的區別,證明就是知道條件和結論,根據一定的規則或標準,由公理和定理推匯出結論的過程。對應到程式設計中即知道輸入和輸出,證明輸出是由輸入計算所得。零知識證明是一種不洩露輸入並且能直接證明輸出正確的演算法。zkSNARK則是目前零知識證明演算法中驗證效率最高的演算法,所以在區塊鏈上有很好的應用場景。DxChain一直積極探索儲存證明等技術,零知識證明也是我們重點研究的方向之一。在這系列文章中我們會盡可能詳細直白的介紹零知識證明,第一篇我們從基本原理開始,後續系列將介紹實際應用中需要做的最佳化,讓我們能夠對零知識證明有比較完整的認識。1. 零知識證明有什麼用大概瞭解了什麼是零知識證明,但是它有什麼用,它能做到哪些一般演算法做不到的事?下面對零知識證明的應用場景列舉一二:
1)關於資料儲存證明:在區塊鏈分散式儲存中,當我們需要驗證礦工的儲存能力時,為了防止礦工作弊,需要讓他們參與大量的計算,但是驗證時又需要能快速有效地驗證,零知識證明正好具有這樣的特性,Filecoin共識的底層就是zkSNARK。2)關於隱私資料的宣告:某個軟體需要使用者芝麻信用分大於700,但是使用者不希望洩露自己的信用分750,又想證明自己有資格使用該軟體。3)匿名認證:使用者持有私鑰,平臺只儲存使用者提交的私鑰雜湊值。使用者登入時只需要證明自己持有的私鑰所生成的雜湊值在平臺的雜湊值列表中即可登入,不需要向平臺提交其他資訊。4)計算外包:將高成本的計算外包出去,只需要對計算結果進行快速驗證,是一種零信任計算。從上面的應用場景來看,在解決資料儲存和隱私保護方面,零知識證明有著極大的應用前景,尤其是區塊鏈,驗證鏈上的資料往往需要多個節點共同參與,零知識證明不但可以快速驗證,還能很好的保護隱私資料。zkSNARK的核心是多項式證明,接下來我們從最簡單的問題開始,先了解多項式的零知識證明,然後試著將一般的演算法程式轉為多項式進行證明。本系列文章會盡量簡單直觀的介紹數學原理,讀者只需要有基本的數學知識即可,但零知識證明的原理還是會有一些抽象,如果不理解可以多讀一兩遍。下面請大家準備好筆和紙,讓我們開始吧~
2. 證明的媒介-多項式假設Alice知道一組長度為10的陣列,Alice聲稱10位數都置為了1,Bob一次只能驗證一位。想要驗證Alice的宣告就要進行抽樣驗證,抽樣一次驗證正確則可信度為1/10,如果要達到95%以上的可信度則需要抽樣10次,如果這個陣列長度達到幾百萬,那抽樣驗證效率就非常低。如何做到對一組數驗證一次就可以達到95%以上的可信度?我們來看透過多項式驗證。例如:f(x) = x^3 - 6x^2 + 11x - 6,這個多項式最高為3次方,則稱它的階為3。多項式的特性是兩個階都為d的不相等多項式,它們的交點個數不會超過d。

比如將原來多項式稍微修改一下變為x^3 - 6x^2 + 10x - 5,兩個多項式的影象如下圖:

我們可以令兩個多項式相等來找到它們的交點,得出 x = 1,即兩個多項式只有一個交點(1, 0)。

對於這兩個多項式,最多有三個不同的x取值可以讓結果相等(實際這裡只有一個),其他任意取值的x導致的計算結果都不相同。因此當一個Prover(證明者)聲稱他知道Verifier(驗證者)也知道的多項式,即他知道該多項式的係數(知道所有n個係數即可確定一個n階多項式),這裡階數越高係數越多,無論係數多少個,他們都可以按下面的協議進行驗證:

Verifier選擇一個隨機值r並在本地計算多項式的結果
Verifier將r傳給Prover,並讓Prover計算相關的多項式結果
Prover代入r到多項式計算並將結果返回給Verifier
Verifier檢查本地計算結果和Prover返回的結果是否相等,如果相等則說明Prover的陳述有較高可信度

比如將r的取值範圍定為[1, 10^77],多項式階數為d,P和V計算得到不同結果的數量可以有10^77-d個,因此如果他們多項式不相等,則碰巧計算結果相等的概率為d/10^77,我們認為幾乎不可能。

這裡只需要驗證一次就可以達到幾乎100%的可信度,所以zkSNARK的核心就是用多項式作為證明媒介。

3.  證明多項式的知識

如上所述,多項式的知識就是它的係數。如果一個人說他知道一個多項式(例如:c1x^1 + c0=0),就意味著他知道多項式的係數c1、c0。

一個比較簡單的定理是,任意一個多項式只要有解,就可以分解成多個線性多項式(一階多項式的影象是直線所以叫線性多項式),即可以將任意多項式看成其因式的乘積:

(x - a0)(x - a1)...(x - an) = 0

只要任意一個因式為0則整個多項式等於0,也就是所有x=a0,a1...an都是多項式的解。

瞭解了這些,接下來讓我們看看這樣一個證明問題:V想要找一個包含解x=1和x=2的多項式,P聲稱自己知道這樣一個多項式,他需要向V證明他知道,但是不希望V馬上知道答案。

假設P知道的多項式為x^3 - 3x^2 + 2x = x(x-1)(x-2),他要證明多項式p(x)包含兩個解x=1和x=2,令t = (x-1)(x-2),則只需要證明p(x) = h(x) * t(x)

這裡h(x) = p(x) / t(x) ,如果不能計算出這樣的h(x),也就意味p(x)不包含t(x),會有餘數(關於多項式除法可以參考[pik14])。

這裡計算出h(x) = x,沒有餘數。說明Prover可以證明他知道這樣一個多項式包含解x=1和x=2。

接下來實踐一遍驗證過程:

Verifier隨機挑選一個數r = 23,計算t = (23 - 1)(23 - 2) = 462,將r發給Prover

Prover計算得到h(x) = p(x)/t(x) = x,分別計算p(r) = p(23) = 10626,h(r) = h(23) = 23,將p(r)和h(r)發給Verifier

Verifier驗證p = h * t : 10626 = 23 * 462 是正確的,驗證透過

這裡如果Prover不知道具體的p(x),則不能計算出匹配的h(x),返回的數值就不會驗證透過。完成驗證後Verifier不知道具體的p(x)和h(x),但是可以確定Prover知道這樣一個多項式,包含解x=1和x=2。

4. 一般程式到多項式-zkSNARK

上面我們講了如何證明多項式的知識,接下來只需要將證明一般的程式問題轉為證明多項式就可以解決大部分場景的證明問題了。

我們用一個簡單問題來走一遍:證明你知道三次方程x^3 + x + 5 == 35的解(答案是3),前提是不透露這個解。

第一步:拍平

拍平就是將任意複雜的表示式轉換為只有兩種運算形式(加法和乘法)。

這裡說的任意複雜的問題是指計算機在有限時間內能解決的計算問題,加法和乘法可以表示其他的運演算法則,比如證明a-b=c,因為證明問題我們是知道結果c的,所以只需要寫成b+c=a就可以,其他比如除法、比較大小等都可以表示,這裡不作展開。

上述方程拍平可以得到:

sym_1 = x * x
y = sym_1 * x
sym_2 = y + x
~out = sym_2 + 5

可以看出來,拍平後的表示式與原始方程是等價的。

第二步:轉為R1CS

一階約束系統R1CS(rank-1 contraint system)是描述向量組之間的約束關係。其實上面的方程或者拍平的表示式就是一種約束系統,這種約束關係確定了只有特定的x滿足約束條件,這裡x=3就是滿足約束的解,將x=3代入拍平後的表示式可以得到一組解向量s:

[~one, x, ~out, sym_1, y, sym_2] 
= [1, 3, 35, 9, 27, 30]

拍平後的四行表示式,每一行是一個乘法門(加法是乘常數1),即一個約束。用R1CS描述原方程就是有四個約束,每一個乘法門約束關係我們都用A*B-C=0表示。例如第一個約束sym_1 = x * x,表示為R1CS為一個向量組:‌‌

A: [0, 1, 0, 0, 0, 0]
B: [0, 1, 0, 0, 0, 0]
C: [0, 0, 0, 1, 0, 0]

將解向量代入應該是滿足這一個約束的(3 * 3 - 9 = 0),如下圖所示

這裡是將解向量代入驗證,用來表示約束關係只需要a、b、c三個向量,所以將四個約束全部表示出來得到如下R1CS向量組:‌‌

A:
[0, 1, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 1, 0]
[5, 0, 0, 0, 0, 1]

B:
[0, 1, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0]

C:
[0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 1]
[0, 0, 1, 0, 0, 0]

可以看出來,我們將乘法門左向量都放到A組,右向量都放到B組,輸出向量都在C組。到這裡我們就成功將一個計算問題轉換成了R1CS,接下來要做的就是證明我們的解滿足這個約束關係。我們不能像上圖那樣把解帶進去驗證四次,這樣的證明不能零知識也不夠簡潔,所以下一步還需要將R1CS轉為多項式。

第三步:R1CS轉為QAP

要將這些向量轉為QAP(quadratic arithmetic program),需要先了解一下拉格朗日插值定理:

經過n個點(x1, y1)、(x2, y2)...(xn, yn)的n-1階多項式為:

比如點(1,2) (2,2) (3,4)對應的多項式為:

知道怎麼用這個公式之後,我們開始將上一步的R1CS轉為QAP。對於ABC三組向量,每組的六個變數分別對應一個多項式,多項式經過四個點,取橫座標x=1,2,3,4,縱座標為向量的值,進行拉格朗日插值:

例如(1, 0) (2, 0) (3, 0) (4, 5) 插值後的多項式為0.833x^3 - 5x^2+9.166x -5,將多項式的係數按冪升序排列,於是有全部的QAP:

A:
[-5.0, 9.166, -5.0, 0.833]
[8.0, -11.333, 5.0, -0.666]
[0.0, 0.0, 0.0, 0.0]
[-6.0, 9.5, -4.0, 0.5]
[4.0, -7.0, 3.5, -0.5]
[-1.0, 1.833, -1.0, 0.166]

B:
[3.0, -5.166, 2.5, -0.333]
[-2.0, 5.166, -2.5, 0.333]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]

C:
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[-1.0, 1.833, -1.0, 0.166]
[4.0, -4.333, 1.5, -0.166]
[-6.0, 9.5, -4.0, 0.5]
[4.0, -7.0, 3.5, -0.5]

此時如果將x=1代入上面18個多項式,可以得到第一個約束的三個先向量,再將x=2,3,4分別代入可以恢復出所有的R1CS。我們可以驗證,當且僅當x=1、2、3、4時,下面關係都是成立的:

設Z(x)=(x-1)(x-2)(x-3)(x-4),則有A(x) * B(x) - C(x) = H * Z(x)

參考上面多項式證明中講的一致性原理,這裡原始方程x^3 + x + 5 ==35的解3決定了A、B、C多項式的係數,我們要驗證的就是Prover知道詳細的A(x)、B(x)、C(x)的具體數值,Verifier透過發起隨機數挑戰就可以驗證Prover知道原方程的解,但是Verifier自始至終無法得到原方程的解。

5. 總結

至此我們理解了zkSNARK的基本原理,這裡也能看出,一個簡單的方程要做到零知識證明需要相對較多的計算,但是證明的效率卻很高。如果希望更快速高效的進行鏈上驗證,包括解決小數誤差的影響,就要用到有限域和橢圓曲線等。後面的系列我們會介紹如何繼續最佳化zkSNARK,讓它可以實際應用起來。

附錄

[Pik14] - Scott Pike. Dividing by a Polynomial. 2014. 

url:http://www.mesacc.edu/~scotz47781/mat120/notes/divide_poly/long_division/long_division.html

[But16] - Vitalik Buterin. Quadratic Arithmetic Programs: from Zero to Hero. 2016.

url:https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649

[But17] - Vitalik Buterin. zk-SNARKs: Under the Hood. 2017. 

url:https://medium.com/@VitalikButerin/zk-snarks-under-the-hood-b33151a013f6

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;