回路の例: 因数分解
早速Circomの回路を見ていきましょう。例えば「33が2つの因数に分解できること」をゼロ知識証明したい場合、以下のような回路が考えられます。
pragma circom 2.1.2;
// 回路の定義
template Multiplier2 () {
// シグナルの宣言
signal input a;
signal input b;
signal output c;
// 制約
c <== a * b;
}
// インスタンス化
component main = Multiplier2();
あくまで因数(1を含む)に分解できるだけで、 \(33 = 1 \cdot 33\) のような分解ができるため、残念ながらRSA暗号において \(n = pq\) の \(p,q\) を知っていることをゼロ知識証明するような用途にはこのままだと使えません。シンプルな回路で、言語仕様を説明することには適しているため、特に変更を加えず説明していきます。
まず、pragma
命令でCircomコンパイラのバージョンを指定しています。もし指定したバージョンの言語仕様に沿っていない機能を使えばエラーになります。この例では、最新バージョンの2.1.2
を指定しています。
次に、template
で回路のテンプレートを定義しています。テンプレートとは、回路を新しく定義する設計図のようなものです。後で説明しますが、component
でインスタンス化して使います。この例では、Multiplier2
という名前の回路を定義しています。
template
の中では、初めにシグナルの宣言をします。シグナルとは、回路の入出力などに使われるもので、有限体 \(\mathbb{F}_p\) の元です。input
が指定されていれば入力シグナルで、output
が指定されていれば出力シグナルです。何も指定しなければ中間シグナルと呼ばれるものになります。シグナルはこの例では、入力シグナルa
,b
と出力シグナルc
を定義しています。
シグナルの宣言の後、制約を記述します。制約とはシグナル間の条件を指定する文です。<==
演算子や==>
演算子を用いて記述できます。注意しなければならないのは、制約はシグナルの2次以下の関数でなければならないということです。つまり、A
,B
,C
という3つのシグナルがあった場合、A * B + C
は許されますが、A * B * C
は許されていません。この例では、c
をa
とb
を掛けた値であるとしています。a * b ==> c
としても良いです。
最後にcomponent
でテンプレートから回路をインスタンス化しています。回路を実行するには、初期コンポーネントが必要で、そのコンポーネントの名前はデフォルトでmain
です。この例では、インスタンス化されたMultiplier2()
が実行されます。