Closed26
snarkjsのtutorialをやってみる
目標
なんとなく概念だけ理解しているSnarksを実際に使えるレベルまで理解する
前提
現状概念的に理解していること
式
- zkSNARKS = Succint Non interactive ARgument of Knowledge
- C(Common Reference String) : generate a proving key(pk) and a verification key(vk) by taking a secret parameters lambda .
- P(Prover): generate proof (pr=P(pk,x,w)) with public input(x)* and private witness(w)
-
V(Verifier): V(vk,x,pr) return true if hte proof(pr) is correct
- →C(x,w)=true
trusted setupについて
- the secret parameter lambda を知っている人は、V(vk,x,pr)がtrueになる虚偽のproofを生成することが可能
- ただ、fake proofを作成できるだけで、private inputはわからない
the secret parameter lambdaはtrusted setupと呼ばれ、この問題のことをtoxic wasteという - trusted setupのセキュリティーを高めるために、不特定多数の人にコミットメントしてもらうのがceremony
- trusted setupが必要ないライブラリーも存在している(zkSTARKs)
結果
- 各要素に関する理解度は高まった。
- circomはかなり雰囲気で書いている。ハッシュの使い方などあっているかわからない。やりたいことと実装の制約を受けそう。
- trusted setupが必要なZKPを使う場合、アプリケーションを動かすまでに時間がかかってしまう
- solidityではhashの裏側の知識がなくてもなんとなく使えるが、circomでは裏側と知ってないとプロダクトレベルのアプリケーションを作成するのは厳しそう。Solidityのassembly書いてるような感じ
- 例えば、なぜnulifierを31にしているのか、Pedersen(248)は何を意味するかなどなど...
- 検証が通った時はとても爽快!!
疑問
- witnessとはなんなのか?(完全には理解できなかった)
-
Phase1とPhase2にceremonyがなぜ別れているのか- Trusted setup for Groth16 SNARKS is done in 2 steps. The first step is universal for all SNARKs and is called Powers of Tau. The second step is called Phase 2 and is circuit-specific, so it should be done separately for each different SNARK.
- zk friendlyなhash関数はpedersonとか、poseidonとかあるみたいだが、どれがメインで使われているものか?
- circuit_final.zkey は公開しても大丈夫か?
- ceremonyの中でもどの情報が漏れるとまずいのか?(contributeする際のrandom text(Entropy)? )
参考
1. power of tau ceremony の開始
snarkjs powersoftau new bn128 14 pot14_0000.ptau -v
- bn128 : curve type
- 14 : ceremoryの最大参加人数。14は2*14=16.384になる。最大は28。これだけ集まったらすごい
pot14_0000.ptauの中身はbinary fileだった
2. ceremony への contribute
snarkjs powersoftau contribute pot14_0000.ptau pot14_0001.ptau --name="First contribution" -v
Enter a random text. (Entropy): first
[DEBUG] snarkJS: Calculating First Challenge Hash
[DEBUG] snarkJS: Calculate Initial Hash: tauG1
[DEBUG] snarkJS: Calculate Initial Hash: tauG2
[DEBUG] snarkJS: Calculate Initial Hash: alphaTauG1
[DEBUG] snarkJS: Calculate Initial Hash: betaTauG1
[DEBUG] snarkJS: processing: tauG1: 0/32767
[DEBUG] snarkJS: processing: tauG1: 16384/32767
[DEBUG] snarkJS: processing: tauG2: 0/16384
[DEBUG] snarkJS: processing: tauG2: 8192/16384
[DEBUG] snarkJS: processing: alphaTauG1: 0/16384
[DEBUG] snarkJS: processing: betaTauG1: 0/16384
[DEBUG] snarkJS: processing: betaTauG2: 0/1
[INFO] snarkJS: Contribution Response Hash imported:
7d33ece0 c684cd08 37fd5965 08e87289
3cece108 2c3099ea f7da51ad 9abb2115
7cecb423 152ed432 4a0ea5e7 c8513df3
9a39082a 0d59d967 661efa9e da6efe95
[INFO] snarkJS: Next Challenge Hash:
75fffa7c c5ead890 2e6b5fec eda1c74d
bc894378 b26fbe67 74ac00ee d7f7a89c
2f395cf4 9b5eb64b e62dd184 e02e91d9
1f8219a0 491cb1a2 eea51651 f69c277c
- entropyをcommitする
- entropyはコマンド実行後に記入できる
-
pot14_0000.ptau
がinputされるファイルで、pot14_0001.ptau
がcommit後のファイル - --name: リファレンスとして利用
3. 2回目のcontribution
snarkjs powersoftau contribute pot14_0001.ptau pot14_0002.ptau --name="Second contribution" -v -e="some random text"
- -e : でentropyを指定できるみたい
これをみんなで繰り返す感じ
trusted setupの必要なzkのサービス作るのは、すごいハードがあるな
4. third party softwareを使って3回目のcontribution
snarkjs powersoftau export challenge pot14_0002.ptau challenge_0003
snarkjs powersoftau challenge contribute bn128 challenge_0003 response_0003 -e="some random text"
snarkjs powersoftau import response pot14_0002.ptau response_0003 pot14_0003.ptau -n="Third contribution name"
- entropyを強固にするためにcontributionに多様性を持たせる
5. これまでのcontributionの検証
snarkjs powersoftau verify pot14_0003.ptau
[INFO] snarkJS: Powers Of tau file OK!
[INFO] snarkJS: Next challenge hash:
dde539d6 8125b3f5 94206538 c3b6d048
923e868b b8520440 a705b519 cee10a1a
4e4bd72c 16be5713 aa6003c1 b95015e0
a39a504e 4de10e3c 392a5fad d9113d57
[INFO] snarkJS: -----------------------------------------------------
[INFO] snarkJS: Contribution #3: Third contribution name
[INFO] snarkJS: Next Challenge:
dde539d6 8125b3f5 94206538 c3b6d048
923e868b b8520440 a705b519 cee10a1a
4e4bd72c 16be5713 aa6003c1 b95015e0
a39a504e 4de10e3c 392a5fad d9113d57
[INFO] snarkJS: Response Hash:
270ef72d cbfdb9d4 91de1f3d ad8c35cf
5ba5d508 e288b582 2d58f0ae 2be9e754
89b6cb84 c6953773 7016a2ee d86fb00b
13276901 5b1ab513 14a889a2 5c06e9d3
[INFO] snarkJS: Response Hash:
223bb4d6 385cd562 ba04d472 a6177907
b40fed6a 78e86e0f ff826a79 7574ccb5
688bec2e 7380b249 3b303baf c6ad67a8
57a4f5cc 32838cdb 8f0ce657 2c5936a3
[INFO] snarkJS: Powers Of tau file OK!
[INFO] snarkJS: -----------------------------------------------------
[INFO] snarkJS: Contribution #2: Second contribution
[INFO] snarkJS: Next Challenge:
223bb4d6 385cd562 ba04d472 a6177907
b40fed6a 78e86e0f ff826a79 7574ccb5
688bec2e 7380b249 3b303baf c6ad67a8
57a4f5cc 32838cdb 8f0ce657 2c5936a3
[INFO] snarkJS: Response Hash:
152c6828 e1a93580 68741d59 32188439
d6d6b9be fb4427b4 5e414062 c3474965
c21c5bc8 6b043e6d 14f7abfb 80e04719
40737b9c 97c07ebd 3a6ced7c 6b80e209
[INFO] snarkJS: Response Hash:
75fffa7c c5ead890 2e6b5fec eda1c74d
bc894378 b26fbe67 74ac00ee d7f7a89c
2f395cf4 9b5eb64b e62dd184 e02e91d9
1f8219a0 491cb1a2 eea51651 f69c277c
[INFO] snarkJS: Powers Of tau file OK!
[INFO] snarkJS: -----------------------------------------------------
[INFO] snarkJS: Contribution #1: First contribution
[INFO] snarkJS: Next Challenge:
75fffa7c c5ead890 2e6b5fec eda1c74d
bc894378 b26fbe67 74ac00ee d7f7a89c
2f395cf4 9b5eb64b e62dd184 e02e91d9
1f8219a0 491cb1a2 eea51651 f69c277c
[INFO] snarkJS: Response Hash:
7d33ece0 c684cd08 37fd5965 08e87289
3cece108 2c3099ea f7da51ad 9abb2115
7cecb423 152ed432 4a0ea5e7 c8513df3
9a39082a 0d59d967 661efa9e da6efe95
[INFO] snarkJS: Response Hash:
bc0bde79 80381fa6 42b20975 91dd83f1
ed15b003 e15c3552 0af32c95 eb519149
2a6f3175 215635cf c10e6098 e2c612d0
ca84f1a9 f90b5333 560c8af5 9b9209f4
[INFO] snarkJS: -----------------------------------------------------
[WARN] snarkJS: this file does not contain phase2 precalculated values. Please run:
snarkjs "powersoftau preparephase2" to prepare this file to be used in the phase2 ceremony.
[INFO] snarkJS: Powers of Tau Ok!
ちゃんとできてる!
6. random beacon の適応
snarkjs powersoftau beacon pot14_0003.ptau pot14_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon"
- a random beacon = a source of public randomness that is not available before a fixed time.
- 遅延ハッシュ関数?
- 10 : 2^10 回反復して実行
- ここまででフェーズ1完了
7. phase 2 の準備
snarkjs powersoftau prepare phase2 pot14_beacon.ptau pot14_final.ptau -v
- ここまでで一番処理が重い
- 今までのcontributionを用いて、特定の計算を暗号化している?数学的に使えるようにしている?
- 最終的なアウトプットはthe circuit proving and verification keys
実際にプロダクトローンチするときはこのプロセスはワクワクするだろうなー
8. final ptau の検証
snarkjs powersoftau verify pot14_final.ptau
これにてtrusted setupは終了!
9. circuit の作成
cat <<EOT > circuit.circom
pragma circom 2.0.0;
template Multiplier(n) {
signal input a;
signal input b;
signal output c;
signal int[n];
int[0] <== a*a + b;
for (var i=1; i<n; i++) {
int[i] <== int[i-1]*int[i-1] + b;
}
c <== int[n-1];
}
component main = Multiplier(1000);
EOT
10. Compile the circuit
circom circuit.circom --r1cs --wasm --sym
- r1cs : 回路の制約システムのbinary file
- wasm : witnessを生成するためのwasmファイルの生成
- sym : symbolファイル. r1csを読みやすくするファイル
11. circuitの確認
=> % snarkjs r1cs info circuit.r1cs 22:56:44
[INFO] snarkJS: Curve: bn-128
[INFO] snarkJS: # of Wires: 1003
[INFO] snarkJS: # of Constraints: 1000
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Labels: 1004
[INFO] snarkJS: # of Outputs: 1
- 今回、'a,b' はpublic inputなので、publicが2、privateが0になっている
12. constraintsの確認
snarkjs r1cs print circuit.r1cs circuit.sym
- constraintsを理解できてないので要復習
13. r1csをjsonに変換
snarkjs r1cs export json circuit.r1cs circuit.r1cs.json
cat circuit.r1cs.json
14. witnessの計算
- inputの作成
cat <<EOT > input.json
{"a": 3, "b": 11}
EOT
- witnessの作成
cd circuit_js
node generate_witness.js circuit.wasm ../input.json ../witness.wtns
- 生成されたwitnessをr1csで確認
cd ..
snarkjs wtns check circuit.r1cs witness.wtns
疑問
- witnessはproofを生成するごとに必要?
15. Setup
proofを生成するためのsetup。Gwoth16は各回路毎にceremoryが必要
// Plonk
snarkjs plonk setup circuit.r1cs pot14_final.ptau circuit_final.zkey
// Fflonk
snarkjs fflonk setup circuit.r1cs pot14_final.ptau circuit.zkey
// Groth 16
snarkjs groth16 setup circuit.r1cs pot14_final.ptau circuit_0000.zkey
- zkey : proving keyとverification keyがある
今回はGroth16を選択します
16 ~ phase 2 ceremony
// first contribute
snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st Contributor Name" -v
// second contribute
snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="Second contribution Name" -v -e="Another random entropy"
// third 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"
// verity zkey
snarkjs zkey verify circuit.r1cs pot14_final.ptau circuit_0003.zkey
// apply random beacon
snarkjs zkey beacon circuit_0003.zkey circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2"
// verify final zkey
snarkjs zkey verify circuit.r1cs pot14_final.ptau circuit_final.zkey
// export verification key
snarkjs zkey export verificationkey circuit_final.zkey verification_key.json
疑問
- phase1とphase2のceremonyの違い
23. proofの作成
// plonk
snarkjs plonk prove circuit_final.zkey witness.wtns proof.json public.json
// fflonk
snarkjs fflonk prove circuit.zkey witness.wtns proof.json public.json
// groth16
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json
- proof.json : proofが含まれている
- public.json : public inputとoutputが含まれている
24. proofの証明
snarkjs plonk verify verification_key.json public.json proof.json
or
snarkjs fflonk verify verification_key.json public.json proof.json
or
snarkjs groth16 verify verification_key.json public.json proof.json
実際の決行結果。OKって出てる!
% snarkjs groth16 verify verification_key.json public.json proof.json 10:22:54
[INFO] snarkJS: OK!
25. Solidityのverifierを生成
snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol
生成されたSolidityファイル
// SPDX-License-Identifier: GPL-3.0
/*
Copyright 2021 0KIMS association.
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
snarkJS is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
snarkJS is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity >=0.7.0 <0.9.0;
contract Groth16Verifier {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// Verification Key data
uint256 constant alphax = 18712674356861626896757009025687602725210820216304635264620479592450406329388;
uint256 constant alphay = 21201160479174563788085618814045375892998967271910465805550111138485812562384;
uint256 constant betax1 = 6397767952456345111109890018805147657449482188663200202099441941382684922249;
uint256 constant betax2 = 1031914164219788346728201756331336046254081657552143577869826750276546580376;
uint256 constant betay1 = 10314878877228187607647061624746478475559001084584511583082950439560380411240;
uint256 constant betay2 = 18647239902447966464243128842451132848313496915470151596628105008731376078483;
uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant deltax1 = 14133835417711969824957902269297611337435395223355120168018193341228421792743;
uint256 constant deltax2 = 14489197459206398478831836706999721375348172822070685587229927545739695521998;
uint256 constant deltay1 = 14876284108737680636580574787030636686006864903929862157929504818767194594337;
uint256 constant deltay2 = 18773531984080155592167771121180341848472115244847951389567955172215104945974;
uint256 constant IC0x = 9537301685309826448508211352626541730374335540610193167484208189129108696235;
uint256 constant IC0y = 1952344178142922204566422550369216688006328914188700713187179877639647350870;
uint256 constant IC1x = 7179809393906979726414314139910248955072516835648097610001921229168923677096;
uint256 constant IC1y = 9139291606196857545346718115880521495052143453800812335542582109766699660651;
// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;
uint16 constant pLastMem = 896;
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[1] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
mstore(0, 0)
return(0, 0x20)
}
}
// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
let _pPairing := add(pMem, pPairing)
let _pVk := add(pMem, pVk)
mstore(_pVk, IC0x)
mstore(add(_pVk, 32), IC0y)
// Compute the linear combination vk_x
g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0)))
// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
// B
mstore(add(_pPairing, 64), calldataload(pB))
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
// alpha1
mstore(add(_pPairing, 192), alphax)
mstore(add(_pPairing, 224), alphay)
// beta2
mstore(add(_pPairing, 256), betax1)
mstore(add(_pPairing, 288), betax2)
mstore(add(_pPairing, 320), betay1)
mstore(add(_pPairing, 352), betay2)
// vk_x
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
// gamma2
mstore(add(_pPairing, 448), gammax1)
mstore(add(_pPairing, 480), gammax2)
mstore(add(_pPairing, 512), gammay1)
mstore(add(_pPairing, 544), gammay2)
// C
mstore(add(_pPairing, 576), calldataload(pC))
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
// delta2
mstore(add(_pPairing, 640), deltax1)
mstore(add(_pPairing, 672), deltax2)
mstore(add(_pPairing, 704), deltay1)
mstore(add(_pPairing, 736), deltay2)
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
isOk := and(success, mload(_pPairing))
}
let pMem := mload(0x40)
mstore(0x40, add(pMem, pLastMem))
// Validate that all evaluations ∈ F
checkField(calldataload(add(_pubSignals, 0)))
checkField(calldataload(add(_pubSignals, 32)))
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
mstore(0, isValid)
return(0, 0x20)
}
}
}
26. verification callのsimulate
snarkjs zkey export soliditycalldata public.json proof.json
["0x0ced66b67be26ba389655ae6cd691bce15f7d74d97386298c6dce1d08c5c4159", "0x03b54cc7d51b23a098610508ca7f8aebbeede7957e4cbdca2d4ed949bb5e8da6"],[["0x297ddc4b1632f13102e94fb43badf46cdcbce73e2be532127c0eb3a25157e630", "0x07d7b75f11585ea1c13c686376237bafb6b0e1310b0bfaa0869973c4ee7527aa"],["0x2d206c8f003d6b9ba75d2b6c5af4f780843cb52780ade3ae4f5ad0f978ba23a2", "0x10ee655c7e7791a11656898ccc3bff9b1117853384c40b9ae455c3933811dd6a"]],["0x295b4dd1b274370a738efdc3a316edb10073752ef06104df7612645af7ba24e4", "0x01e2e6bef279af98d203cf1834bf2b42a8a5ce6ff211eb2a5f1de7e3b53f2819"],["0x110d778eaf8b8ef7ac10f8ac239a14df0eb292a8d1b71340d527b26301a9ab08"]
実さにRemixで試してみたらOKでた!いえい!
extra. 他のinputでも試してみる
tutorialは26までだったが、他のinputでも試してみる。
input.jsonを下記に変更する。
{"a": 2, "b": 5}
proofの生成コマンドが下記なので、おそらくwitnessを作成してproofを作成する必要がありそう。
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json
下記でwitnessとproofの生成
cd circuit_js
node generate_witness.js circuit.wasm ../input.json ../witness.wtns
cd ..
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json
コマンドで検証もOK
% snarkjs groth16 verify verification_key.json public.json proof.json 12:01:04
[INFO] snarkJS: OK!
solidityでも検証OK
snarkjs zkey export soliditycalldata public.json proof.json 12:01:12
["0x2b2513f0f33bfc25e7c81ad9ccb097a9829d7ca5c507d0187448910d5df3d306", "0x1fd8fa7544bf488786d9439cbac505ee8801e76b4f9f6f2b9f3e2afab28ad9f8"],[["0x02bc53faaca394d0a5590d284bdc6adf48a12861e205a536aad510970a402800", "0x270b7481ffd318531e996a1acf19277d8efbdcf482545471664b8fe0198d1cb3"],["0x26e22fdb64748352270c9767cf89700f627b97b7acf0a6037cfec77fd200c8ed", "0x1c2addc89a9ff4a52baa1ff3b32e077d0308f738ae24c1c12a4e9c8e91075264"]],["0x1dfb4ee55e879d7630e0284919f032fb6bafe3c92b662fa67192b93349d9f659", "0x0baf9937752a47a1feb5319a64438884598388775131b6cfee721047912b4740"],["0x1b616cda5326dd65179e81b6c84754a4e71ffd776951b5949fb1d4aebb1a5830"]
素晴らしい!!
extra 2. public input private inputで試してみる
サンプルの回路は膨大な計算の結果が正しいことを証明するものであった。つまり、計算量の圧縮 がメイン。
tornado cashのように、情報の秘匿の文脈で、private inputとpublic inputの回路を作成してみる。
回路
pragma circom 2.0.0;
include "./node_modules/circomlib/circuits/bitify.circom";
include "./node_modules/circomlib/circuits/pedersen.circom";
template SecretHasher() {
signal input secret;
signal output secretHash;
component secretHasher = Pedersen(248);
component secretBits = Num2Bits(248);
secretBits.in <== secret;
for (var i = 0; i < 248; i++) {
secretHasher.in[i] <== secretBits.out[i];
}
secretHash <== secretHasher.out[0];
}
template Main() {
signal input secretHash;
signal input secret;
component hasher = SecretHasher();
hasher.secret <== secret;
secretHash === hasher.secretHash;
}
component main {public [secretHash]} = Main();
- secretをprivate, secretHashをpublicにして、secretを持っているかの証明をする回路
pedersen hashの生成
secret生成スクリプトはこちら
const snarkjs = require('snarkjs');
const crypto = require('crypto');
const circomlib = require('circomlib');
/** Generate random number of specified byte length */
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes));
/** Compute pedersen hash */
const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0];
const main = () => {
const secret = rbigint(31);
const hash = pedersenHash(secret.leInt2Buff(31));
console.log({secret, hash})
}
main();
注意input.json
input.jsonの値は""で囲む必要あり。囲んでなくて半日潰しました...
input.json
{
"secretHash": "19453594387736930619599466479227236853954336336379209455419802476697169222580",
"secret": "307007605995433306163765064475084281166882259195083713142912403572210678838"
}
これでも成功しました!!!
検証が通った時の爽快感半端ない!!!!
最終的に必要なファイルの整理
ファイルの整理
- ceremonyで利用したファイルはcircuit_final.zkeyとverification_key.jsonに集約されてそう
- witnessの生成に必要 : circuit.wasm, *input.json → witness.wtns の生成
- proofの生成に必要 : circuit_final.zkey, witness.wtns → proof.json, public.json の生成
- proofの証明に必要 : verification_key.json, public.json, proof.json
必要なファイルは下記だと思う
- circuit.wasm
- circuit_final.zkey
- verification_key.json
extra.3 nodeでも検証
スクリプト
const snarkjs = require("snarkjs");
const fs = require("fs");
async function run() {
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
{
"secretHash": "19453594387736930619599466479227236853954336336379209455419802476697169222580",
"secret": "307007605995433306163765064475084281166882259195083713142912403572210678838"
},
"circuit_js/circuit.wasm",
"circuit_final.zkey"
);
console.log("Proof: ");
console.log(JSON.stringify(proof, null, 1));
const vKey = JSON.parse(fs.readFileSync("verification_key.json"));
const res = await snarkjs.groth16.verify(vKey, publicSignals, proof);
if (res === true) {
console.log("Verification OK");
} else {
console.log("Invalid proof");
}
}
run().then(() => {
process.exit(0);
});
実行結果
node prove.js 15:16:52
Proof:
{
"pi_a": [
"86048593150950050416241473609597328465285085619181514193205893362069857571",
"1788633298711708113870817439486285699612181339402462547222797953331671124868",
"1"
],
"pi_b": [
[
"16877126577791499277007026491207892846082136364207622921313219034881942764527",
"11627221366399766622862897735658733668527736339150086600857836881335501243706"
],
[
"8151184030359756013363479050756600295631267721992385801030064926734001663143",
"10579943309315321115337047601815312554968185820256372855285038387730557873138"
],
[
"1",
"0"
]
],
"pi_c": [
"18967710012431226672710835636120361308820851234332074607104613596313474743256",
"8352153606170882598292782926671891849774642674591250568454485445181181313544",
"1"
],
"protocol": "groth16",
"curve": "bn128"
}
Verification OK
できた!!!
proofの生成からsolidity verificationまで
cd circuit_js &&
node generate_witness.js circuit.wasm ../input.json ../witness.wtns &&
cd .. &&
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json &&
snarkjs groth16 verify verification_key.json public.json proof.json &&
snarkjs zkey export soliditycalldata public.json proof.json
このスクラップは2023/11/10にクローズされました