はじめに
この記事では、zk-SNARKのライブラリであるhalo2で使用される多項式コミットメントスキーム(Polynomial Commitment Scheme; PCS)について解説します([1]のAppendix 2)。
PCSは、名前の通り、多項式のコミットメント(特定の性質を持つ多項式を知っていたことを後で証明できるもの)を伝えるプロトコルです。
halo2の実際の実装と論文の相違点はドキュメント[3]に説明があります。
halo2のPCSとアキュムレーションスキーム
halo2のPCSは入力長の対数に比例するラウンド数でメッセージのやりとりを抑えるように構成されています。このプロトコルは、特殊な形式の内積アーギュメントを利用することで、「アキュムレータacci
」を用いる、Accumulation schemes[1]を実現しています。そして、halo2ではこのAccumulation schemesを利用することでSNARKが構成されています。
アキュムレータは、証明の対象の列q1,q2⋯
において、i
までの対象が正しかったことを示すことができ、(i)その検証時間が通常のqi
の検証よりも短い、(ii)証明のサイズが増加しない、という二つの性質を持っています。
アキュムレーションスキームは、以下の3つのアルゴリズムによって構成されています。
- 証明者:新しいインスタンスとそれまでのアキュムレータから、新しいアキュムレータを作成
- 検証者:新しいインスタンスとそれまでのアキュムレータ、証明者によって作成されたアキュムレータの整合性を検証
- 決定者:証明の最後でアキュムレータに蓄積された証明が正しいかどうかを判断
つまり、アキュムレータは「証明を次々と蓄積していき、それまでの証明が正しいことと、新しい証明が正しいことを一回の計算で検証できるような証明」と言えます。内積アーギュメントを利用したアキュムレーションスキームでは多項式コミットメントを作成します。アキュムレーションスキームのアルゴリズムはそれぞれ以下の様に動作します。
- 証明者:ランダムオラクルを用いて得たチャレンジを利用して、新しいインスタンスとそれまでのアキュムレータから新しい多項式コミットメントを作成することで新しいアキュムレータを作成
- 検証者:(i)内積アーギュメントの検証、(ii)ランダムオラクルを用いて生成されたチャレンジを利用した多項式コミットメントの整合性の検証、によって新しいインスタンスとそれまでのアキュムレータの整合性を検証
- 決定者:証明の最後で、アキュムレータのコミットメント内の多項式を評価点での値を調べることでアキュムレータに蓄積された証明が正しいかどうかを判断
内積アーギュメントを用いたアキュムレーションスキームは、コミットメントC
、このコミットメント内の多項式p
、評価点z
、評価値v
、p(z)=v
である事を示す証明π
からなる証明対象のインスタンスと同じフォーマット(C,z,v,π)
を持ちます。このアキュムレーションスキームでは、離散対数問題に基づくPCSである、PCDL
を用いており、PCDL
を用いて多項式コミットメントC
が作成されます。
過去のアキュムレータと新たなインスタンスを(C1,z1,v1,π1),(C2,z2,v2,π2)
、この証明π1,π2
から復元された多項式をh1(X),h2(X)
ランダムオラクルを用いて作成されたチャレンジをα
とします。すると、アキュムレーションスキームで作成される新たなアキュムレータでは、π1,π2
から復元したコミットメントをU1,U2
とすると新たなコミットメントがU1+αU2
、対応する多項式がh1(X)+αh2(X)
となり、ランダムオラクルから得た評価点z
での値はv=h1(z)+αh2(z)
となります。そして証明π
はp(z)=v
を示すPCDL
のOpen
の操作の結果となり、これらを合わせた(C,z,v,π)
が新たなアキュムレータとなります。
さらにhalo2では、評価点での値が0になるような、ランダムに選択した多項式を用いることで(Openの2の操作)、PCDL
にhidingと呼ばれる性質を持たせ、SNARKのゼロ知識性を実現しています。
本稿ではこのPCDL
の解説を試みます。
内積アーギュメント
以降、G
を位数p
の巡回群(例えば有限体上の楕円曲線の有理点のなす群)とします。
本稿で解説するPCSで用いられる内積アーギュメントは、証明者と検証者が生成元のベクトルG,H∈Gn
と生成元U∈G
を共有し証明者が予めコミットメントとして
P=Σi=0nai⋅Gi+Σi=0nbi⋅Hi+⟨a,b⟩⋅U(1)
を伝えておき、後でこれらの内積の値を証明者が検証者にc=⟨a,b⟩
として送ると、検証者が、「証明者が、コミットメントを作成した時点で、内積の値がc
となるベクトルa,b
を知っていたこと」を検証できるような、対話型のプロトコルです。
メッセージのやりとりのラウンド数が入力長の対数で抑えられるようなこのような内積アーギュメントの例として、Bulletproofs[2]があります。
詳しくは[4]の解説を参照してください。
PCSの解説
PCDL
の目標は以下を実現することです。
- 証明者が多項式
p
に対するコミットメントC
を検証者に伝えておく。
- 後で検証者が選択した評価点
z
に対して証明者がv=p(z)
を検証者に送り返すと、検証者がコミットメントC
を利用することで証明者がC
を作成した時点で多項式p
を知っていたことが分かるが、多項式p
自体は知ることができない。
PCDL
は
- Setup(証明のフォーマットから、コミットメントの公開パラメータを作成)
- Trim(コミットメントの公開パラメータの中から、コミットメントの作成に用いる鍵を選択)
- Commit(多項式から、その多項式のコミットメントを作成)
- Open(多項式に評価点を代入し、開示を行う)
- Check(Openで得られた開示とCommitで得られたコミットメントを用いて、証明者が特定の多項式を知っていたことを検証)
の5つの手順から成ります。
Setup
証明者と検証者が共同で行う操作です。
群を選択する際に用いられるセキュリティーパラメータλ
と多項式の最大の次数D
を受け取って、公開パラメータとして
- 群
G
、その次数q
、その群を代表する生成元G
- 多項式の係数と結合される生成元
G0,⋯,GD
- 多項式の係数を秘密にするための生成元
S
- 評価値と結合される生成元
H
を返します。
Trim
証明者と検証者が共同で行う操作です。
多項式の次数を表すD
以下の値d
を受け取り、Setupの結果の、多項式と結合される生成元G0,⋯GD
の中からd+1
個を選び、改めてG0,⋯Gd
。と置きます。
以降SetupとTrimの操作で得られた(G,q,G,(G0,⋯Gd),S,H)
をコミットメントキーとします。
Commit
証明者が行う操作です。
コミットメントの対象となる多項式p
、乱数ω
を受け取り、p
の係数をc=(c0,⋯cd)
としたとき(つまりp(X)=Σi=0dciXi
)、内積アーギュメントのコミットメント(1)のうち、Σi=0nai⋅Gi
の項でai=ci,Gi=Gi(i=0,⋯d)
とおき、多項式の係数を秘密にするために乱数ω
と生成元S
の積を加えたC:=Σi=0dci⋅Gi+ω⋅S
をコミットメントとします。
Open
証明者が行う操作です。
多項式自体は秘密に保ったまま、検証者が選んだ評価点で多項式を評価したときの評価値を伝えることで、コミットメント作成時に、多項式を知っていたことを伝えます。
検証者が選んだ評価点z
とCommitの結果、コミットを行ったときに用いた乱数ω
を用いて、以下の操作を行います。
- 多項式
p
にz
を代入したv=p(z)
を計算する。
- 多項式
p(z)=0
を満たす多項式p
と、そのコミットメントに用いる乱数ω
をランダムに選ぶ(多項式の係数を秘密にするための操作)
- 2.で選んだ
p,ω
にCommitを行い、その結果をC
とする。
- ランダムオラクル
ρ
を用いてチャレンジα=ρ0(C,z,v,C)
を作成
- 開示する多項式
p′:=p+αp
を計算し、係数をΣi=0dciXi
とする(開示する多項式から元の多項式が分からない様にしている).
- 開示する多項式に対応するように乱数も計算し
ω′:=ω+αω
とする。
- 証明者の不正(*)を防ぐために、評価値と結合される生成元
H
を変更する。Checkのときとその値を一致させるため、p′
のコミットメントから乱数に影響される項を取り除いたC′:=C+αC−ω′S
をランダムオラクルの入力に与えて得た0番目のチャレンジξ0:=ρ(C′,z,v)
を用いてH′=ξ0H
と変更する。
- 内積アーギュメントをFiat-Shamir変換によって対話型から非対話型に変換したプロトコルによって、
L,R,U,c
を作成する。
- 最終的な出力として手順9の結果に
C,ω′
を加えた(L,R,U,c,C,ω′)
を出力する。
この出力を用いて検証者は
C+v⋅H′=Σi=0dci⋅Gi+ω⋅S+p(z)⋅H′(2)
の検証によって、証明者がコミットメントのC
の作成時にp(z)=v
を満たすような多項式p
を知っていたことを検証できます。
手順8の、内積アーギュメントをFiat-Shamir変換によって対話型から非対話型に変換したプロトコルによるコミットメントの作成は以下の手順で行われます。以下、ベクトルa=(a1,⋯an)
の左半分をl(a)=(a1,⋯an/2)
、右半分をr(a)=(an/2+1,⋯an)
と表します。
まずc0:=(c0,⋯,cd),z0:=(1,z⋯,zd),G0:=(G0,⋯,Gd)
と初期化したあと、i=1,⋯log(d+1)
の順に以下の手順を繰り返します。
- 前の手順で作られたベクトルの半分
l(Gi−1),r(ci−1),l(zi−1)
を用いたコミットメントとして
Li:=commitl(Gi−1)∣∣H′(r(ci−1)∣∣⟨r(ci−1),l(zi−1))⟩)=⟨l(Gi−1),r(ci−1)⟩+⟨r(ci−1),l(zi−1))⟩H′
を計算する。
- 1と同様に
r(Gi−1),l(ci−1),r(zi−1)
を用いたコミットメントとして
Ri:=commitr(Gi−1)∣∣H′(l(ci−1)∣∣⟨l(ci−1),r(zi−1))⟩)=⟨r(Gi−1),l(ci−1)⟩+⟨l(ci−1),r(zi−1))⟩H′
を計算する。
- ランダムオラクル
ρ0
を用いてチャレンジξi:=ρ0(ξi−1,Li,Ri)
を作成
- 次のラウンドで用いるためのベクトル
Gi:=l(Gi−1)+ξi⋅r(Gi−1),ci:=l(ci−1)+ξi−1⋅r(ci−1),zi:=l(zi−1)+ξi⋅r(zi−1)
を作成
以上の手順によって、最終的にU:=Glog(d+1),c:=clog(d+1)
としてLlog(d+1),Rlog(d+1),U,c
を出力します。
ここで防いでいる証明者の不正
ここで、Openの7の操作(*)で防いでいる、証明者の不正の例を挙げます。
悪意のある証明者が知っている多項式p(X)=Σi=0dciXi
に対する本来のコミットメントC=Σi=0dci⋅Gi+ω⋅S
の代わりに公開されている生成元H
と適当な値t
を使った偽のコミットメントC~:=Σi=0dci⋅Gi+t⋅H+ω⋅S
をコミットしたとします。7の操作を行なわず、生成元H
を変更しなかった場合には、Openの操作で証明者が本来の値v=p(z)
の代わりにv~=p(z)−t
を送ることで
C~+v~⋅H=Σi=0dci⋅Gi+t⋅H+ω⋅S+(p(z)−t)⋅H=C+v⋅H
より、(2)が成り立つので、検証者が偽の証明を受理してしまいます。したがって悪意のある証明者の「v
を代入すると値がp(z)−t
となるような多項式を知っていた」という偽の証明が成功します。
Check
検証者が行う操作です。
多項式の次数d
、証明者のコミットメントC
、検証者が送った評価点z
で証明者が多項式の評価を行なった値v
、Openの操作で作成したその証明(L,R,U,c,C,ω′)
を受け取り、以下のように非対話型の内積アーギュメントの検証を行います。
ここで、SetupとTrimの操作で得られたコミットメントキーは(G,q,G,(G0,⋯Gd),S,H)
です。
- 多項式の次数
d
とコミットメントキーのベクトル(G0,⋯Gd)
の長さが対応していること(d=∣(G0,⋯Gd)∣−1
であること)を確認する。
- ランダムオラクル
ρ0
を用いてチャレンジα=ρ0(C,z,v,C)
を復元する。(Openの手順4に対応)
- 同様に、
p′
のコミットメントから乱数に影響される項を取り除いたC′:=C+αC−ω′S
、0番目のチャレンジξ0:=ρ(C′,z,v)
、生成元H′=ξ0H
を復元する。(Openの手順8に対応)
- 内積アーギュメントの検証を行う。
C0:=C+v⋅H′
と置き、i=1,⋯log(d+1)
に対して以下の操作を繰り返す。(Openの手順9に対応)
- チャレンジ
ξi:=ρ0(ξi−1,Li,Ri)
を作成する。
- コミットメント
Ci:=ξ−1Li+Ci−1+ξiRi
を復元する。
- 多項式
h(X):=∏i=0log(d+1)−1(1+ξlog(d+1)−iX2i)
を定義する。
- 評価点として
v′:=c⋅h(z)
を計算し、5で得られた最後のコミットメントClog(d+1)
について、Clog(d+1)=c⋅U+v′⋅H′
が成り立つことを検証する。
- 多項式
h(X)
のi
次の係数をhi
とおくときU=Σi=0dhi⋅Gi
が成り立つことを検証する。
Checkの簡単な解説
Checkの手順5で定義した多項式h(X):=∏i=0log(d+1)−1(1+ξlog(d+1)−iX2i)
には「その係数ベクトルとG0
の内積が、Glog(d+1)
と一致する」という性質を持ちます。
例えば、多項式p
が3次式、d=3,log(d+1)=2
の場合には、
G0=(G1,G2,G3,G4)
とすると、
G1=(G1,G2)+ξ1(G3,G4)=(G1+ξ1G3,G2+ξ1G4)
G2=l(G1)+ξ2r(G1)=G1+ξ2G2+ξ1G3+ξ1ξ2G4
となります。一方、このとき
h(X):=∏i=01(1+ξ2−iX2i)
=(1+ξ2X)(1+ξ1X2)
=1+ξ2X+ξ1X2+ξ1ξ2X3
となるので、係数ベクトルを
h=(1,ξ2,ξ1,ξ1ξ2)
とおくとG2=⟨h,G0⟩
が成り立ちます。
Checkの手順7ではこれを利用して、U=Glog(d+1)
が⟨h,G0⟩=Σi=0dhi⋅Gi
と一致することを検証しています。
疑問点
- halo2のPCSで使われている内積アーギュメントはBulletproofsとよく似た形ではありますが、厳密には異なっています。この形の内積アーギュメントがアーギュメントとして成立しているのかどうかまでは確認できていません。
- 対話型のプロトコルである、内積アーギュメントを非対話型に変換する際に、ランダムオラクルを用いたFlat-Shamir変換を行なっているようですが、この変換の正当性、安全性について理解していません。Flozen Heartと呼ばれる脆弱性とも関連しているようです。
- このPCSは[5]で与えられている、PCSの特殊な場合となっているようです。[5]ではIPAを一般化した、Generalized Inner Product Argument (GIPA)を提唱し、GIPAをPCSへの応用しています。しかし、詳細は理解しきれていません。
参考文献
[1]Proof-Carrying Data from Accumulation Schemes
[2]Bulletproofs: Short Proofs for Confidential Transactions and More
[3]halo2 - The halo2 Book
[4]内積アーギュメント Bulletproofsについて
[5]Proofs for Inner Pairing Products and Applications