Halo 2の中で使われている多項式コミットメントスキーム

お知らせ
Table of Contents

はじめに

この記事では、zk-SNARKのライブラリであるhalo2で使用される多項式コミットメントスキーム(Polynomial Commitment Scheme; PCS)について解説します([1]のAppendix 2)。
PCSは、名前の通り、多項式のコミットメント(特定の性質を持つ多項式を知っていたことを後で証明できるもの)を伝えるプロトコルです。
halo2の実際の実装と論文の相違点はドキュメント[3]に説明があります。

halo2のPCSとアキュムレーションスキーム

halo2のPCSは入力長の対数に比例するラウンド数でメッセージのやりとりを抑えるように構成されています。このプロトコルは、特殊な形式の内積アーギュメントを利用することで、「アキュムレータacc_i」を用いる、Accumulation schemes[1]を実現しています。そして、halo2ではこのAccumulation schemesを利用することでSNARKが構成されています。

アキュムレータは、証明の対象の列q_1, q_2\cdotsにおいて、iまでの対象が正しかったことを示すことができ、(i)その検証時間が通常のq_iの検証よりも短い、(ii)証明のサイズが増加しない、という二つの性質を持っています。

アキュムレーションスキームは、以下の3つのアルゴリズムによって構成されています。

  1. 証明者:新しいインスタンスとそれまでのアキュムレータから、新しいアキュムレータを作成
  2. 検証者:新しいインスタンスとそれまでのアキュムレータ、証明者によって作成されたアキュムレータの整合性を検証
  3. 決定者:証明の最後でアキュムレータに蓄積された証明が正しいかどうかを判断

つまり、アキュムレータは「証明を次々と蓄積していき、それまでの証明が正しいことと、新しい証明が正しいことを一回の計算で検証できるような証明」と言えます。内積アーギュメントを利用したアキュムレーションスキームでは多項式コミットメントを作成します。アキュムレーションスキームのアルゴリズムはそれぞれ以下の様に動作します。

  1. 証明者:ランダムオラクルを用いて得たチャレンジを利用して、新しいインスタンスとそれまでのアキュムレータから新しい多項式コミットメントを作成することで新しいアキュムレータを作成
  2. 検証者:(i)内積アーギュメントの検証、(ii)ランダムオラクルを用いて生成されたチャレンジを利用した多項式コミットメントの整合性の検証、によって新しいインスタンスとそれまでのアキュムレータの整合性を検証
  3. 決定者:証明の最後で、アキュムレータのコミットメント内の多項式を評価点での値を調べることでアキュムレータに蓄積された証明が正しいかどうかを判断

内積アーギュメントを用いたアキュムレーションスキームは、コミットメントC、このコミットメント内の多項式p、評価点z、評価値vp(z) = vである事を示す証明\piからなる証明対象のインスタンスと同じフォーマット(C, z, v, \pi)を持ちます。このアキュムレーションスキームでは、離散対数問題に基づくPCSである、\text{PC}_{DL}を用いており、\text{PC}_{DL}を用いて多項式コミットメントCが作成されます。

過去のアキュムレータと新たなインスタンスを(C_1, z_1, v_1, \pi_1),(C_2, z_2, v_2, \pi_2)、この証明\pi _1, \pi _2から復元された多項式をh_1(X), h_2(X)ランダムオラクルを用いて作成されたチャレンジを\alphaとします。すると、アキュムレーションスキームで作成される新たなアキュムレータでは、\pi _1, \pi _2から復元したコミットメントをU_1, U_2とすると新たなコミットメントがU_1+\alpha U_2、対応する多項式がh_1(X)+\alpha h_2(X)となり、ランダムオラクルから得た評価点zでの値はv = h_1(z)+\alpha h_2(z)となります。そして証明\pip(z) = vを示す\text{PC}_{DL}Openの操作の結果となり、これらを合わせた(C, z, v, \pi)が新たなアキュムレータとなります。

さらにhalo2では、評価点での値が0になるような、ランダムに選択した多項式を用いることで(Openの2の操作)、\text{PC}_{DL}にhidingと呼ばれる性質を持たせ、SNARKのゼロ知識性を実現しています。

本稿ではこの\text{PC}_{DL}の解説を試みます。

内積アーギュメント

以降、\mathbb{G}を位数pの巡回群(例えば有限体上の楕円曲線の有理点のなす群)とします。
本稿で解説するPCSで用いられる内積アーギュメントは、証明者と検証者が生成元のベクトルG,H\in \mathbb{G}^nと生成元U\in \mathbb{G}を共有し証明者が予めコミットメントとして
P = \Sigma _{i=0}^{n}a_i\cdot G_i +\Sigma _{i=0}^{n}b_i\cdot H_i + \braket{\vec{a},\vec{b}} \cdot U \;\;\;(1)
を伝えておき、後でこれらの内積の値を証明者が検証者にc = \braket{\vec{a},\vec{b}}として送ると、検証者が、「証明者が、コミットメントを作成した時点で、内積の値がcとなるベクトル\vec{a},\vec{b}を知っていたこと」を検証できるような、対話型のプロトコルです。

メッセージのやりとりのラウンド数が入力長の対数で抑えられるようなこのような内積アーギュメントの例として、Bulletproofs[2]があります。
詳しくは[4]の解説を参照してください。

PCSの解説

\text{PC}_{DL}の目標は以下を実現することです。

  1. 証明者が多項式pに対するコミットメントCを検証者に伝えておく。
  2. 後で検証者が選択した評価点zに対して証明者がv=p(z)を検証者に送り返すと、検証者がコミットメントCを利用することで証明者がCを作成した時点で多項式pを知っていたことが分かるが、多項式p自体は知ることができない。

\text{PC}_{DL}

  1. Setup(証明のフォーマットから、コミットメントの公開パラメータを作成)
  2. Trim(コミットメントの公開パラメータの中から、コミットメントの作成に用いる鍵を選択)
  3. Commit(多項式から、その多項式のコミットメントを作成)
  4. Open(多項式に評価点を代入し、開示を行う)
  5. Check(Openで得られた開示とCommitで得られたコミットメントを用いて、証明者が特定の多項式を知っていたことを検証)

の5つの手順から成ります。

Setup

証明者と検証者が共同で行う操作です。
群を選択する際に用いられるセキュリティーパラメータ\lambdaと多項式の最大の次数Dを受け取って、公開パラメータとして

  1. \mathbb{G}、その次数q、その群を代表する生成元G
  2. 多項式の係数と結合される生成元G_0,\cdots ,G_{D}
  3. 多項式の係数を秘密にするための生成元S
  4. 評価値と結合される生成元H

を返します。

Trim

証明者と検証者が共同で行う操作です。
多項式の次数を表すD以下の値dを受け取り、Setupの結果の、多項式と結合される生成元G_0,\cdots G_{D}の中からd+1個を選び、改めてG_0,\cdots G_{d}。と置きます。

以降SetupとTrimの操作で得られた(\mathbb{G}, q, G, (G_0,\cdots G_{d}), S, H)をコミットメントキーとします。

Commit

証明者が行う操作です。
コミットメントの対象となる多項式p、乱数\omegaを受け取り、pの係数を\vec{c} = (c_0, \cdots c_d)としたとき(つまりp(X) = \Sigma _{i=0}^{d}c_i X^i)、内積アーギュメントのコミットメント(1)のうち、\Sigma _{i=0}^{n}a_i\cdot G_iの項でa_i = c_i, G_i = G_i(i = 0, \cdots d)とおき、多項式の係数を秘密にするために乱数\omegaと生成元Sの積を加えたC := \Sigma _{i=0}^{d}c_i\cdot G_i + \omega \cdot Sをコミットメントとします。

Open

証明者が行う操作です。
多項式自体は秘密に保ったまま、検証者が選んだ評価点で多項式を評価したときの評価値を伝えることで、コミットメント作成時に、多項式を知っていたことを伝えます。

検証者が選んだ評価点zとCommitの結果、コミットを行ったときに用いた乱数\omegaを用いて、以下の操作を行います。

  1. 多項式pzを代入したv = p(z)を計算する。
  2. 多項式\overline{p}(z)=0を満たす多項式\overline{p}と、そのコミットメントに用いる乱数\overline{\omega}をランダムに選ぶ(多項式の係数を秘密にするための操作)
  3. 2.で選んだ\overline{p}, \overline{\omega}にCommitを行い、その結果を\overline{C}とする。
  4. ランダムオラクル\rhoを用いてチャレンジ\alpha = \rho _0 (\overline{C}, z, v, C)を作成
  5. 開示する多項式p':=p + \alpha \overline{p}を計算し、係数を\Sigma _{i=0}^{d}c_i X^iとする(開示する多項式から元の多項式が分からない様にしている).
  6. 開示する多項式に対応するように乱数も計算し\omega ':=\omega + \alpha \overline{\omega}とする。
  7. 証明者の不正(*)を防ぐために、評価値と結合される生成元Hを変更する。Checkのときとその値を一致させるため、p'のコミットメントから乱数に影響される項を取り除いたC ':=C + \alpha \overline{C} - \omega ' Sをランダムオラクルの入力に与えて得た0番目のチャレンジ\xi _0 := \rho (C', z, v)を用いてH'=\xi _0 Hと変更する。
  8. 内積アーギュメントをFiat-Shamir変換によって対話型から非対話型に変換したプロトコルによって、\vec{L}, \vec{R}, U, cを作成する。
  9. 最終的な出力として手順9の結果に\overline{C}, \omega 'を加えた(\vec{L}, \vec{R}, U, c,\overline{C}, \omega ' )を出力する。

この出力を用いて検証者は
C + v\cdot H'= \Sigma _{i=0}^{d}c_i\cdot G_i + \omega \cdot S + p(z)\cdot H'\;\;\;(2)
の検証によって、証明者がコミットメントのCの作成時にp(z) = vを満たすような多項式pを知っていたことを検証できます。

手順8の、内積アーギュメントをFiat-Shamir変換によって対話型から非対話型に変換したプロトコルによるコミットメントの作成は以下の手順で行われます。以下、ベクトル\vec{a} = (a_1,\cdots a_n)の左半分をl(\vec{a}) =(a_1,\cdots a_{n/2})、右半分をr(\vec{a})=(a_{n/2 + 1},\cdots a_{n})と表します。
まず\vec{c}_0 := (c_0, \cdots, c_d), \vec{z}_0 := (1, z\cdots, z^d), \vec{G}_0 := (G_0, \cdots, G_d)と初期化したあと、i=1,\cdots \log (d+1)の順に以下の手順を繰り返します。

  1. 前の手順で作られたベクトルの半分l(\vec{G}_{i-1}), r(\vec{c}_{i-1}), l(\vec{z}_{i-1})を用いたコミットメントとして
    L_i := commit_{l(\vec{G}_{i-1})||H'}(r(\vec{c}_{i-1})||\braket{r(\vec{c}_{i-1}),l(\vec{z}_{i-1}))}) = \braket{l(\vec{G}_{i-1}), r(\vec{c}_{i-1})} + \braket{r(\vec{c}_{i-1}),l(\vec{z}_{i-1}))} H'
    を計算する。
  2. 1と同様にr(\vec{G}_{i-1}), l(\vec{c}_{i-1}), r(\vec{z}_{i-1})を用いたコミットメントとして
    R_i := commit_{r(\vec{G}_{i-1})||H'}(l(\vec{c}_{i-1})||\braket{l(\vec{c}_{i-1}),r(\vec{z}_{i-1}))}) = \braket{r(\vec{G}_{i-1}), l(\vec{c}_{i-1})} + \braket{l(\vec{c}_{i-1}),r(\vec{z}_{i-1}))} H'
    を計算する。
  3. ランダムオラクル\rho_0を用いてチャレンジ\xi _i := \rho _0 (\xi _{i-1}, L_{i}, R_{i})を作成
  4. 次のラウンドで用いるためのベクトル\vec{G}_i := l(\vec{G}_{i-1}) + \xi _i\cdot r(\vec{G}_{i-1}), \vec{c}_i := l(\vec{c}_{i-1}) + \xi _i^{-1}\cdot r(\vec{c}_{i-1}), \vec{z}_i := l(\vec{z}_{i-1}) + \xi _i\cdot r(\vec{z}_{i-1})を作成

以上の手順によって、最終的にU:=G_{\log (d+1)}, c:=c_{\log (d+1)}として\vec{L}_{\log (d+1)}, \vec{R}_{\log (d+1)}, U, cを出力します。

ここで防いでいる証明者の不正

ここで、Openの7の操作(*)で防いでいる、証明者の不正の例を挙げます。

悪意のある証明者が知っている多項式p(X) = \Sigma _{i=0}^{d}c_i X^iに対する本来のコミットメントC = \Sigma _{i=0}^{d}c_i\cdot G_i + \omega \cdot Sの代わりに公開されている生成元Hと適当な値tを使った偽のコミットメント\tilde{C} := \Sigma _{i=0}^{d}c_i\cdot G_i + t\cdot H + \omega \cdot Sをコミットしたとします。7の操作を行なわず、生成元Hを変更しなかった場合には、Openの操作で証明者が本来の値v = p(z)の代わりに\tilde{v} = p(z)-tを送ることで
\tilde{C} + \tilde{v}\cdot H = \Sigma _{i=0}^{d}c_i\cdot G_i + t\cdot H + \omega \cdot S + (p(z)-t)\cdot H=C+v\cdot Hより、(2)が成り立つので、検証者が偽の証明を受理してしまいます。したがって悪意のある証明者の「vを代入すると値がp(z)-tとなるような多項式を知っていた」という偽の証明が成功します。

Check

検証者が行う操作です。
多項式の次数d、証明者のコミットメントC、検証者が送った評価点zで証明者が多項式の評価を行なった値v、Openの操作で作成したその証明(\vec{L}, \vec{R}, U, c,\overline{C}, \omega ' )を受け取り、以下のように非対話型の内積アーギュメントの検証を行います。
ここで、SetupとTrimの操作で得られたコミットメントキーは(\mathbb{G}, q, G, (G_0,\cdots G_{d}), S, H)です。

  1. 多項式の次数dとコミットメントキーのベクトル(G_0,\cdots G_{d})の長さが対応していること(d = |(G_0,\cdots G_{d})| - 1であること)を確認する。
  2. ランダムオラクル\rho _0を用いてチャレンジ\alpha = \rho _0 (\overline{C}, z, v, C)を復元する。(Openの手順4に対応)
  3. 同様に、p'のコミットメントから乱数に影響される項を取り除いたC ':=C + \alpha \overline{C} - \omega ' S、0番目のチャレンジ\xi _0 := \rho (C', z, v)、生成元H'=\xi _0 Hを復元する。(Openの手順8に対応)
  4. 内積アーギュメントの検証を行う。C_0 := C + v\cdot H'と置き、i = 1, \cdots \log (d+1)に対して以下の操作を繰り返す。(Openの手順9に対応)
    • チャレンジ\xi _i := \rho _0 (\xi _{i-1}, L_{i}, R_{i})を作成する。
    • コミットメントC_i := \xi ^{-1}L_i + C_{i-1}+ \xi _i R_iを復元する。
  5. 多項式h(X) := \prod _{i=0} ^{\log (d+1) -1} (1+ \xi _{\log (d+1)-i} X^{2^i})を定義する。
  6. 評価点としてv' := c\cdot h(z)を計算し、5で得られた最後のコミットメントC_{\log (d+1)}について、C_{\log (d+1)}=c\cdot U + v'\cdot H'が成り立つことを検証する。
  7. 多項式h(X)i次の係数をh_iとおくときU = \Sigma _{i=0}^d h_i\cdot G_iが成り立つことを検証する。

Checkの簡単な解説

Checkの手順5で定義した多項式h(X) := \prod _{i=0} ^{\log (d+1) -1} (1+ \xi _{\log (d+1)-i} X^{2^i})には「その係数ベクトルと\vec{G}_0の内積が、\vec{G}_{\log (d+1)}と一致する」という性質を持ちます。

例えば、多項式pが3次式、d = 3, \log (d+1) =2の場合には、
\vec{G}_0 = (G_1, G_2, G_3, G_4)
とすると、
\vec{G}_1 = (G_1, G_2)+\xi_1(G_3, G_4) = (G_1+\xi_1 G_3, G_2+\xi_1 G_4)
\vec{G}_2 = l(\vec{G}_1)+\xi_2r(\vec{G}_1) = G_1 + \xi_2G_2 + \xi_1G_3 + \xi_1\xi_2G_4
となります。一方、このとき
h(X) := \prod _{i=0} ^{1} (1+ \xi _{2-i} X^{2^i})
\;\;\;\;\;\;=(1+ \xi _{2} X)(1+ \xi _{1} X^{2})
\;\;\;\;\;\;=1+ \xi _{2} X+ \xi _{1} X^{2} + \xi _{1}\xi _{2} X^3
となるので、係数ベクトルを
\vec{h}=(1,\xi _{2},\xi _{1},\xi _{1}\xi _{2})とおくと\vec{G}_2=\braket{\vec{h}, \vec{G}_0}が成り立ちます。

Checkの手順7ではこれを利用して、U = G_{\log (d+1)}\braket{\vec{h}, \vec{G}_0}=\Sigma _{i=0}^d h_i\cdot G_iと一致することを検証しています。

疑問点

  • 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

タイトルとURLをコピーしました