collaborative-circomを触ってみた
概要
この記事ではcollaborative-circom(co-circomと呼ばれたりする)のサンプルコードを実行し、一連の流れを紹介します。
ローカルでMPCを構成し、secret sharing->proof->verifyまでやっていきましょう。
collaborative-circomとは?
co-circomはcollaborative SNARKをcircomで実行可能にしたツールです。
オーストリアのTaceoLabsが提供しています。
以下のような機能を提供しています。
- zkとMPCを組み合わせた秘密計算ができる
- Circomのシンタックスを変えたりしなくて良い
- Snark.js、Solidity Veriferなどの環境も使える
- Ciromのシンタックスはそのままで良い。groth16とPlonkも実行可能
- 複数の参加者が共同でinputを提供できる
zkもMPCじゃんという話は一旦置いておきます
collaborative SNARKとは?
MPCはREP3 MPCというShamierの秘密分散法に近い線形秘密分散法をベースとしており、複数の当事者がそれぞれの入力を非公開にしたまま、共同でProofを生成することができます。Circuitへのinputを秘密分散法におけるshareに分割し、他の当事者へ共有します。それぞれの当事者はこのshareを元にProofを作成することができます。
詳細な原理はこちらの論文を参照ください。
最近ではその点を改善した手法も提案されています。HyperPlonkを使っているようですね。
このアプローチだと大規模なCircuitも組めそう。
Installation
先述の通り、CircuitやProofの生成は普段通りのCircom+SnarkJSで実装可能です。まずは公式の通りに進めて下さい。
最後に $sudo mv co-circom /usr/local/bin/
を実行して任意のプロジェクトから呼び出せるようにしておくと便利です。
以下のようにバージョンが確認できれば完了です。
$co-circom -V
co-circom 0.5.0
Example
今回はPoseidon Hash(groth16)のサンプルを実行してみようと思います。
Poseidon Hash:https://scrapbox.io/bitpickers/Poseidon_Hash
まずはローカルにクローンして下さい。
$ git clone https://github.com/TaceoLabs/collaborative-circom.git
$ cd co-circom/co-circom/examples
さて、一連の処理を定義したshell scriptが用意されているので各実行ステップを見ていきましょう
ちなみにexamplesのメンテが間に合っていないようで、pathを更新しなければなりませんでした。
PRを投げているのでご参照ください。以降のサンプルコードはこのブランチとします。
Circuit
今回のcirocom実装はこんな感じで至ってシンプルです。
pragma circom 2.0.0;
include "poseidon.circom";
component main = Poseidon(2);
exampleではすでにコンパイルされ、r1csやzkeyファイル、verification_key.json
が用意されています。
もし自前で用意したcircuitを使いたい場合は以下のように実行して下さい。circomに触れたことがある方にとっては不便ないと思います。
$circom poseidon.circom --r1cs
Network
MPCのNetwork Configはこのようにtomlファイルで定義されます。
この辺から普通のCircomとの変化を楽しむことができます。
[compiler]
allow_leaky_loops = false
link_library = ["./lib"]
[vm]
allow_leaky_logs = false
[network]
my_id = 0
bind_addr = "0.0.0.0:10000"
key_path = "./data/key0.der"
[[network.parties]]
id = 0
# normally we would use DNS name here such as localhost, but localhost under windows is resolved to ::1, which causes problems since we bind to ipv4 above
dns_name = "127.0.0.1:10000"
cert_path = "./data/cert0.der"
[[network.parties]]
id = 1
dns_name = "127.0.0.1:10001"
cert_path = "./data/cert1.der"
[[network.parties]]
id = 2
dns_name = "127.0.0.1:10002"
cert_path = "./data/cert2.der"
詳細なフィールドについてはこちらを参照ください。
MPC Partyのセットアップに際し、configをmy_id
をユニークにしてpartyの数だけ用意します。
このサンプルでは3PCとなっています。
split input into shares
MPCにおけるPreprocessing phaseです。
今回はシンプルな3PCなので一つのinputファイルを元に3つのshareに分割していきます。
input.json
を用意して
{
"inputs": [
"-0x1515",
"0x8392ef9d"
]
}
以下のコマンドを実行します。
$cargo run --release --bin co-circom -- split-input --circuit groth16/test_vectors/poseidon/circuit.circom --input groth16/test_vectors/poseidon/input.json --protocol REP3 --curve BN254 --out-dir groth16/test_vectors/poseidon --config groth16/test_vectors/poseidon/config.toml
上記のコマンドではここまでみてきたcircuit,input,network configのファイルを受け取り、REP3 MPCプロトコルを使って秘密分散しています。
出力先に3つのファイル(input.json.0.shared、input.json.1.shared、input.json.2.shared)が生成されます。
このような実行結果が出れば成功です。
INFO run_split_input: co_circom: 333: Split input into shares successfully
run witness extension in MPC
ここからはMPCにおけるOnline phaseです。
先ほど作成したのsharesを使ってMPCでsecret-shared witnessファイルを生成します。
# run witness extension in MPC
$cargo run --release --bin co-circom -- generate-witness --input groth16/test_vectors/poseidon/input.json.0.shared --circuit groth16/test_vectors/poseidon/circuit.circom --protocol REP3 --curve BN254 --config configs/party1.toml --out groth16/test_vectors/poseidon/witness.wtns.0.shared &
cargo run --release --bin co-circom -- generate-witness --input groth16/test_vectors/poseidon/input.json.1.shared --circuit groth16/test_vectors/poseidon/circuit.circom --protocol REP3 --curve BN254 --config configs/party2.toml --out groth16/test_vectors/poseidon/witness.wtns.1.shared &
cargo run --release --bin co-circom -- generate-witness --input groth16/test_vectors/poseidon/input.json.2.shared --circuit groth16/test_vectors/poseidon/circuit.circom --protocol REP3 --curve BN254 --config configs/party3.toml --out groth16/test_vectors/poseidon/witness.wtns.2.shared
以下のようなログが出力され各partyがそれぞれ異なるwitnessファイルを得ていることがわかります。
INFO run_generate_witness: co_circom: 536: Party 0: Witness extension took 22.247 ms
INFO run_generate_witness: co_circom: 536: Party 2: Witness extension took 22.607 ms
INFO run_generate_witness: co_circom: 536: Party 1: Witness extension took 22.776 ms
INFO run_generate_witness: co_circom: 401: Witness successfully written to groth16/test_vectors/poseidon/witness.wtns.0.shared
INFO run_generate_witness: co_circom: 401: Witness successfully written to groth16/test_vectors/poseidon/witness.wtns.2.shared
INFO run_generate_witness: co_circom: 401: Witness successfully written to groth16/test_vectors/poseidon/witness.wtns.1.shared
run proving in MPC
いよいよProof生成です。
$cargo run --release --bin co-circom -- generate-proof groth16 --witness groth16/test_vectors/poseidon/witness.wtns.0.shared --zkey groth16/test_vectors/poseidon/poseidon.zkey --protocol REP3 --curve BN254 --config configs/party1.toml --out proof.0.json --public-input public_input.json &
cargo run --release --bin co-circom -- generate-proof groth16 --witness groth16/test_vectors/poseidon/witness.wtns.1.shared --zkey groth16/test_vectors/poseidon/poseidon.zkey --protocol REP3 --curve BN254 --config configs/party2.toml --out proof.1.json &
cargo run --release --bin co-circom -- generate-proof groth16 --witness groth16/test_vectors/poseidon/witness.wtns.2.shared --zkey groth16/test_vectors/poseidon/poseidon.zkey --protocol REP3 --curve BN254 --config configs/party3.toml --out proof.2.json
ここでも各partyがそれぞれproofを生成していることがわかります。
INFO run_generate_proof: co_circom: 502: Party 2: starting proof generation..
INFO run_generate_proof: co_circom: 502: Party 1: starting proof generation..
INFO run_generate_proof: co_circom: 502: Party 0: starting proof generation..
INFO run_generate_proof: co_circom: 506: Party 1: Proof generation took 63.335 ms
INFO run_generate_proof: co_circom: 506: Party 2: Proof generation took 63.895 ms
INFO run_generate_proof: co_circom: 506: Party 0: Proof generation took 63.842 ms
INFO run_generate_proof: co_circom: 542: Wrote proof to file proof.1.json
INFO run_generate_proof: co_circom: 635: Proof generation finished successfully
INFO run_generate_proof: co_circom: 542: Wrote proof to file proof.0.json
INFO run_generate_proof: co_circom: 542: Wrote proof to file proof.2.json
INFO run_generate_proof: co_circom: 635: Proof generation finished successfully
INFO run_generate_proof: co_circom: 630: Wrote public inputs to file public_input.json
INFO run_generate_proof: co_circom: 635: Proof generation finished successfully
Finished `release` profile [optimized] target(s) in 0.28s
verify proof
最後にProofを検証しましょう。
# verify proof
$cargo run --release --bin co-circom -- verify groth16 --proof proof.0.json --vk groth16/test_vectors/poseidon/verification_key.json --public-input public_input.json --curve BN254
INFO run_verify: co_circom: 693: Proof verification took 5.084 ms
INFO run_verify: co_circom: 714: Proof verified successfully
まとめ
複数のprivate inputを元にProofを生成したり、secure computingの実行結果からsmart contractを発火させるなどzkpを用いたユースケースが広がりそうです。
Circomに馴染みがある方であれば割と簡単に使いこなせるのではないでしょうか。
他にもshareを統合する機能や異なるMPCプロトコルに変換する機能(REP3からShamier's Secret Sharingへの変換など)が提供されています。
今後は単一のProverで実行した際の比較やベンチマークをとっていきたいです。
Discussion