天天看點

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

前言:本文我們使用兩個公開庫circom、snarkjs嘗試建構一個簡單的零知識證明。其中,circom庫用于建構和編譯代數電路,snarkjs庫是zk-snarks協定的獨立實作。本文使用的方法主要參考的是snarkjs庫0.4.6官方教程(英文)和博文circom與snarkjs經典教程(中文)。感謝這兩篇文獻的作者。

另,github上面也給出了一個較為完整的英文版的tutorial,感興趣的盆友可以點選連結​​​​​​​學習。

目錄

準備:安裝Node.js

1. 安裝circom&snarkjs

2. 可信設定

3.  建構電路 

4. 生成groth16算法密鑰 

5. 計算見證(witness)

6. 證明與驗證

準備:安裝Node.js

具體方法參考我的另一篇博文。

1. 安裝circom&snarkjs

在終端輸入如下安裝指令:

npm install -g circom
npm install -g snarkjs
           

在執行第二條指令時出現git報錯:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

  上網搜尋發現可能原因是(1) ipv6,(2) 之前開了網絡代理,git預設走代理通道,導緻現在關掉代理之後用不了了。

方法1:在終端輸入如下指令禁用ipv6:

networksetup -setv6off Wi-Fi
           

方法2: 首先檢視git配置,在終端輸入“git config --global --list ”,若發現傳回内容中包含http.https或者https.https字樣,說明設定了網絡代理。輸入如下指令撤掉代理即可:

git config --global --unset http.proxy
git config --global --unset https.proxy
           

我遇到的是問題(1),使用方法1解決了。

2. 可信設定

2.1 建立一個新檔案夾,用于包含後續所有檔案:

mkdir factor
cd factor
           

2.2 執行new指令:

snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
           

* 解釋:

[new]:用于開啟一個新的 powers of tau ceremony (該叫法摘自snarkjs庫0.4.6官方教程)

[bn128]:選擇bn128曲線(可選的還有bls12-381曲線,bn和bls的簡介見後面“補充”部分)

[12]:規定了我們接下來要建構的電路的兩個限制。限制1:電路中constraints(稍後會解釋)的數量不能多于

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

個;限制2:電路中parameter的數量不能多于

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

個。

* 效果:

此條指令會建立一個pot12_0000.ptau檔案,執行指令後會列印以下字段:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

 * 補充

參考博文zk-SNARK零知識證明曲線選擇及Electric Coin Co. 官網的相關内容,Barreto-Naehrig (BN) 和Barreto-Lynn-Scott (BLS)都是pairing-friendly橢圓曲線。不同之處在于,BN曲線若想要達到128-bit的安全性,需要q≈2384,相應的BN曲線的階數r也會提高到2384量級,r值的增大會影響multi-exponentiation、FFT等運算性能,進而影響zk-SNARK以及安全多方計算的執行效率,同時也會影響key檔案不必要的增大。 而對于BLS曲線,當q≈2384 且 embedding degree k=12時,具有128-bit的安全性,而相應的階數r≈2256,遠小于BN曲線的2384量級。

2.3 執行contribute指令

snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
           

* 解釋:

[contribute] 該指令将上一步建立的pot12_0000.ptau檔案作為輸入,并輸出一個新的ptau檔案pot12_0001.ptau。ptau檔案包含所有已經進行過的challenge和response計算。

[name] 這一字段内你可以任意填入一些内容。在後面執行驗證(第6步)的時候這個内容會被列印出來。

* 效果:

執行此條指令,會被要求輸入一些随機内容,輸入後列印如下内容:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

2.4 第二次contribute

snarkjs powersoftau contribute pot12_0001.ptau pot12_0002.ptau --name="Second contribution" -v -e="some random text"
           

* 解釋:

此條指令與上一條功能相同,不同點在于,加入了[-e]指令,直接輸入了一些随機值(上一步是互動式地輸入随機值,這一步直接将随機值寫入了指令中)。

* 效果:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

2.5 第三次contribute(第三方contribute)

snarkjs powersoftau export challenge pot12_0002.ptau challenge_0003
snarkjs powersoftau challenge contribute bn128 challenge_0003 response_0003 -e="some random text"
snarkjs powersoftau import response pot12_0002.ptau response_0003 pot12_0003.ptau -n="Third contribution name"
           

 不太确定這步跟上兩步的異同,但是執行完成後會生成pot12_0003.ptau檔案:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

2.6 驗證

snarkjs powersoftau verify pot12_0003.ptau
           

如果驗證通過,在列印資訊的第一行會顯示“[INFO] snarkJS: Powers Of tau file OK!” :

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

2.7 引入random beacon

snarkjs powersoftau beacon pot12_0003.ptau pot12_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon"
           

 此條指令會生成一個pot12_beacon.ptau檔案。

2.8 生成公開資訊

snarkjs powersoftau prepare phase2 pot12_beacon.ptau pot12_final.ptau -v
           

* 解釋:

[prepare phase2]:生成多項式在點

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

、點

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

、點

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

的值的密文。

此指令将pot12_beacon.ptau檔案作為輸入,生成一個新檔案pot12_final.ptau。

2.9 最終驗證

snarkjs powersoftau verify pot12_final.ptau
           

* 解釋:

在開始下一步(建構電路)之前,執行一個最終驗證。

* 效果:

執行此條指令,部分列印資訊如下:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

3.  建構電路 

我們準備證明的問題是:我們知道數字a和b(秘密),它們相乘得到c(公開值)。

3.1 建立一個circom檔案:

vim circuit.circom
           

檔案内容如下:

1. template Multiplier() {
2.    signal private input a;
3.    signal private input b;
4.    signal output c;
5.    c <== a*b;
6. }
7. 
8. component main = Multiplier();
           

* 解釋:

2-4行:這個電路有兩個private輸入信号,即a和b;有一個輸出信号,即c。

第5行:輸入和輸出使用

<==

運算符進行關聯。 在circom中,<==運算符做兩件事。 首先是連接配接信号。 第二個是施加限制。在本例中,我們使用

<==

c

連接配接到

a

b

,同時将

c

限制為

a * b

的值,即電路做的事情是讓強制信号 

c

 為 

a*b

的值。

第8行:在聲明 

Multiplier

 模闆之後, 我們使用名為

main

的元件執行個體化它。編譯電路時,必須始終有一個名為

main

的元件。

(對第5行和第8行的解釋摘自博文circom與snarkjs經典教程)

3.2 編譯電路

circom circuit.circom --r1cs --wasm --sym -v
           

3.3 用snarkjs檢視編譯後的電路

snarkjs r1cs info circuit.r1cs
           

獲得如下輸出:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&amp;snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

3.4 列印constraints

snarkjs r1cs print circuit.r1cs circuit.sym
           

列印資訊如下:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&amp;snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

3.5 将電路輸出成json格式

snarkjs r1cs export json circuit.r1cs circuit.r1cs.json
cat circuit.r1cs.json
           

執行這兩條語句後會列印json字元串。

4. 生成groth16算法密鑰 

** 步驟4.2--4.7類似于上文步驟2.3--2.7, 2.9

4.1 設定

snarkjs groth16 setup circuit.r1cs pot12_final.ptau circuit_0000.zkey
           

* 解釋:

此指令生成zkey,即零知識證明密鑰,包含證明密鑰(proving key)和驗證密鑰(verification key)。每個電路都單獨生成一個zkey,我們可以驗證一個zkey是否屬于某個電路。

Note that 

circuit_0000.zkey

 (the output of the 

zkey

 command above) does not include any contributions yet, so it cannot be used in a final circuit.

——摘自snarkjs庫0.4.6官方教程

4.2 zkey Contribute

snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st Contributor Name" -v
           

此指令會生成一個新的circuit_0001.zkey檔案,裡面包含了一個contribution。這一步會要求輸入一些随機内容,如下:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&amp;snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

4.3 第二次contribute

snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="Second contribution Name" -v -e="Another random entropy"
           

4.4 第三次contribution(第三方contribute)

snarkjs zkey export bellman circuit_0002.zkey  challenge_phase2_0003
snarkjs zkey bellman contribute bn128 challenge_phase2_0003 response_phase2_0003 -e="some random text"
snarkjs zkey import bellman circuit_0002.zkey response_phase2_0003 circuit_0003.zkey -n="Third contribution name"
           

4.5 驗證

snarkjs zkey verify circuit.r1cs pot12_final.ptau circuit_0003.zkey
           

4.6 引入random beacon

snarkjs zkey beacon circuit_0003.zkey circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2"
           

4.7 驗證最終zkey

snarkjs zkey verify circuit.r1cs pot12_final.ptau circuit_final.zkey
           

4.8 輸出驗證密鑰

snarkjs zkey export verificationkey circuit_final.zkey verification_key.json
           

5. 計算見證(witness)

witness中包含與已知電路所有constraints相比對的信号,可以了解為問題的答案。在我們的例子中,一個見證包含a、b、c。我們先生成witness,然後再用witness生成零知識證明。

5.1 建立input檔案

建立一個json檔案:

vim input.json
           

在json檔案中寫入如下内容并儲存:

{"a": 3, "b": 11}
           

5.2 運作計算指令

snarkjs wtns calculate circuit.wasm input.json witness.wtns
           

5.3 檢查witness計算有無錯誤

snarkjs wtns debug circuit.wasm input.json witness.wtns circuit.sym --trigger --get --set
           

* 效果:

此指令會列印我們輸入的a、b以及相應的輸出c:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&amp;snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

6. 證明與驗證

6.1 建構零知識證明

snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json
           

* 解釋:

這一指令生成檔案proof.json和public.json,前者包含證明,後者包含公開輸入和公開輸出。

* 注:

第5步(計算witness)和第6.1步(生成證明)可以合并為一條語句:

snarkjs groth16 fullprove input.json circuit.wasm circuit_final.zkey proof.json public.json
           

6.2 驗證

snarkjs groth16 verify verification_key.json public.json proof.json
           

* 效果:

若驗證成功将列印“OK!”:

ZK-SNARKS | 建立第一個零知識snark電路目錄準備:安裝Node.js1. 安裝circom&amp;snarkjs2. 可信設定3.  建構電路 4. 生成groth16算法密鑰 5. 計算見證(witness)6. 證明與驗證

繼續閱讀