【ゼロ知識証明】Circomを使った回路の生成と証明とコントラクトの検証(第14回)

Web3

  1. 今回のブログ記事の内容
    1. Circomとは?
      1. 独自のプログラミング言語
      2. snarkjsとの連携
      3. Solidityコードの自動生成
      4. zkSNARKsプロトコル対応
  2. Circom/snarkjsを使った回路と証明の流れ
    1. 手順
      1. 手順
    2. 概要
      1. Gitからリポジトリを取得
      2. ライブラリのインストール
      3. 回路生成と検証
      4. スクリプトの説明
        1. zkkey.sh
        2. zkbuild.sh
        3. zkprove.sh
        4. zkverify.sh
        5. zkcreatestamacon.sh
        6. zkall.sh
      5. 生成されたファイルの確認
      6. 生成されるファイル
      7. 各種ファイルの処理の流れ
        1. 回路(処理)
        2. インプット
        3. 出力
        4. プルーフ
    3. Circom回路コードの例
      1. 掛け算
        1. 「signal input a; 」「signal input b;」
        2. 「signal output c」
        3. c <= a * b;
        4. component main {public [a]} = Multiplier2();
          1. 回路をビルド
      2. 3乗の掛け算
        1. signal t;
        2. t <= in * in;
        3. out <= t * in + in + 5;
          1. 回路をビルド
      3. If-else文
        1. component eq = IsEqual();
        2. 「eq.in[0] <= w; 」「eq.in[1] <= 1;」
        3. signal t1; t1 <= a * (b + 3);
        4. signal t2; t2 <= eq.out * t1;
        5. out <= t2 + (1 – eq.out) * (a + b);
          1. 回路をビルド
      4. For文
        1. signal sums[n+1];
        2. sums[0] <= 0;
        3. for (var i = 0; i < n; i++) { sums[i+1] <= sums[i] + in[i]; }
        4. out <= sums[n];
        5. component main = SumUp(4);
          1. 回路をビルド
      5. 値の範囲チェック
        1. le1.in[0] <= lowerBound; le1.in[1] <= in;
        2. le2.in[0] <= in;le2.in[1] <= upperBound;
        3. out <= le1.out * le2.out;
        4. component main = RangeCheck();
          1. 回路をビルド
      6. ハッシュ関数: sha256
        1. signal input in[size]; signal output out[256];
        2. component sha = Sha256(size);
        3. sha.in <= in;
        4. out <= sha.out;
        5. component main = HashSha256(16);
          1. 回路をビルド
      7. ハッシュ関数:mimc
        1. signal input in;signal output out;
        2. component hasher = MiMC7(91);
        3. hasher.x_in <= in;
        4. hasher.k <= 0;
        5. out <= hasher.out;
        6. component main = Hash();
          1. 回路をビルド
  3. コントラクトウォレットの構築と動作
    1. 手順
    2. アプリケーションの構築
      1. ウォレット入りクライアントアプリケーションの作成
        1. 目的
        2. 手順
      2. Circomでの証明生成
        1. 目的
        2. 手順
    3. Circom回路の構築
      1. 回路の設計
        1. 目的
        2. 手順
    4. スマートコントラクトの構築
      1. コントラクトロジックの実装
        1. 目的
        2. 手順
        3. コマンドの実行
      2. デプロイと動作確認
        1. 目的
        2. 手順
    5. アプリケーションからコントラクトを実行するための手順
      1. appディレクトリへの移動とモジュールのインストール:
      2. config.jsonの編集
      3. contractwallet.jsの実行
    6. ETHをデポジットするプロセス
      1. ETHのデポジット
      2. contractwallet.jsの実行
    7. ウォレットの残高をチェックする方法
      1. 残高チェックの準備
      2. contractwallet.jsの実行
      3. 確認結果
    8. 認証用のパスワードハッシュを登録する
      1. パスワードハッシュの登録
      2. コントラクトウォレットの実行
    9. コントラクトウォレットで送金処理を行う方法
      1. snarkjsによる証明の使用
      2. contractwallet.jsの実行
      3. 残高チェック(二回目)
    10. 再度送金を試みる際の手順
      1. 新しいプルーフの生成
      2. 送金処理の実行
  4. まとめ

今回のブログ記事の内容

今回は前回のブログで説明したゼロ知識証明(ZKP)の算術回路の表現とKZGコミットメントを実際にCircomというプログラミング言語を使って、回路を生成し、証明とコントラクトの検証を行ってみようと思います。

Circomとは?

独自のプログラミング言語

Circomは、ゼロ知識証明に関連する処理を記述するための独自のプログラミング言語です。この言語を用いて、ゼロ知識証明の回路を設計・構築することができます。

Circomは、ゼロ知識証明を効率的に実装し、ブロックチェーン上でのプライバシー保護やデータ検証に広く活用されているツールです。

snarkjsとの連携

Circomで記述したコードを、JavaScriptのライブラリであるsnarkjsを使ってコンパイルし、生成されたファイルを取り扱います。これにより、ゼロ知識証明の証明書(ブループ)の生成と検証が可能になります。

Solidityコードの自動生成

Circomは、検証用のコントラクト(スマートコントラクト)に必要なSolidityコードを自動的に生成します。これにより、Ethereumなどのブロックチェーン上での検証をスムーズに行えます。

zkSNARKsプロトコル対応

Circomは、zkSNARKsのプロトコルの一部として、PlonKやGroth16、FFlonkなどのプロトコルを使用可能です。

Circom/snarkjsを使った回路と証明の流れ

手順

手順

概要

Gitからリポジトリを取得

Circomを使ってsnarkjsを使った回路と照明の流れを実際にやってみようと思います。

まずは、GitHubからリポジトリを取得し、Circomの回路生成から検証までの手順を説明します。

最初にGitコマンドを使ってリポジトリをクローンし、Visual Studio Code を使って回路を開くプロセスを紹介します。下のgithubからリポジトリをクローンします。

GitHub - blockchaininnovation/circompractice
Contribute to blockchaininnovation/circompractice development by creating an account on GitHub.

それでは実際にディレクトリを作ってリポジトリをクローンします。

$mkdir lesson
$cd lesson
$lesson clone https://github.com/blockchaininnovation/circompractice.git
$cd circompractice

リポジトリをクローンした後はVscodeを開いてディレクトリの確認を行います。「circompractice」のディレクトリの中に「app」「circom」「contract」の3つのディレクトリがあることを確認します。

ライブラリのインストール

Vscode内のターミナルを起動し、cd circom/ コマンドを使用してcircomディレクトリに移動します。

# circomディレクトリに移動
$cd circom/

次に、npm install コマンドを実行して、必要なライブラリをインストールします。

# 必要なライブラリをインストール
npm install

回路生成と検証

multiply.circom」に対して、トラステッドセットアップ、回路の生成からグループ生成、検証、コントラクト出力のすべてを行うために、「zkall.sh」を使用します。

# 回路生成からグループ生成、検証までを実行
circom$./zkall.sh multiply 12

スクリプトの説明

zkkey.sh

トラステッドセットアップ、コミットメント生成に必要な乱数値を生成。

zkbuild.sh

回路の計算、証明書、証明時に生じるデータを作成。

zkprove.sh

インプット値と入力、証明書を用いてグループを生成。

zkverify.sh

検証鍵でグループを検証。

zkcreatestamacon.sh

Solidityの検証用コントラクトのソースを出力。

zkall.sh

先ほどのコマンド入力で使ったスクリプトで、上記で説明したスクリプトを全て実行します。

生成されたファイルの確認

生成された様々な結果ファイルが「work」ディレクトリに保存され、これらのファイルを確認することで、回路の生成や検証の結果を見ることができます。

生成されるファイル

proof.json(証明):

生成された回路に基づいて、与えられた入力から証明(Proof)が生成されます。

public.json(パブリックインプットとアウトプット):

回路が処理された結果、公開されるべき入力値と出力値が生成されます。

Vertifier.sol(検証用のコントラクトソース):

生成された証明を検証するためのコントラクトコードが生成されます。

各種ファイルの処理の流れ

各種ファイルの処理の流れをを知ることで、Circomを使用した回路の生成とその後の証明や検証のプロセスがどのように管理され、ファイルがどのように生成されるかが明確になります。

この知識は、実際にZK-SNARKsや他の証明システムを使ってプロジェクトを進める上で重要です。

回路(処理)

Circomで記述された回路ファイルは、circom/circuitディレクトリ内に保存されます。

インプット

入力データはcircom/circuit/inputディレクトリ内のJSONファイルとして保存されます。これにはパブリックインプットとプライベートインプットが含まれます。

出力

処理された結果はcircom/work/回路名/public.jsonに保存され、これが公開されるべき値となります。

プルーフ

証明ファイルはcircom/work/回路名/proof.jsonに保存され、後に検証用に使用されます。

Circom回路コードの例

掛け算

コードの場所
circom/circuit/multiply.circom

入力値a, bをかけた結果を出力する回路

「signal input a; 」「signal input b;」

a」と「b」という2つの入力信号を宣言しています。signalは回路内で使用する値を表します。

「signal output c」

掛け算の結果を格納する出力信号「c」を宣言しています。

c <= a * b;

a」と「b」を掛け合わせた結果を「c」に格納します。

component main {public [a]} = Multiplier2();

この部分では、Multiplier2テンプレートを使用してメインの回路を定義しています。「a」は公開される入力(Public Input)として指定され、「b」はプライベート入力(Private Input)として扱われます。

回路をビルド

コマンド を使用して、この回路をビルドおよび実行することができます。

「circom」ディレクトリに移動して以下のコードをコマンドで実行します。

circum$./zkall.sh multiply 12

3乗の掛け算

コードの場所
circom/circuit/cubic.circom

この回路は、入力された値 x に対して、三乗(x3)と一次の加算(x)、および定数加算(+5)を行い、その結果を出力します。

signal t;

中間計算の結果を保持するための信号「t」が定義されています。

t <= in * in;

inの二乗(x2)を計算し、その結果を「t」に格納します。

out <= t * in + in + 5;

t」(つまりx2)と「in」を掛け合わせ、さらに「in」と5を加算した結果を出力信号「out」に格納します。これは、最終的に「 x3+x+5 」を計算していることになります。

回路をビルド
circum$./zkall.sh cubic 12

If-else文

コードの場所
circom/circuit/myfunc.circom

この回路は、プログラミング言語でのif-else条件文をCircomでどのように表現するかを示しています。具体的には、以下のような関数をCircom回路に変換しています。

component eq = IsEqual();

eq」というコンポーネントは、入力された値「w」が「1」であるかどうかを判定するためのものです。ここでは「IsEqual」コンポーネントを使用して「w」と「1」を比較しています。

「eq.in[0] <= w; 」「eq.in[1] <= 1;」

w」が「1」と等しいかどうかを判定するために、「eq」コンポーネントの入力に「w」と「1」を渡しています。

signal t1; t1 <= a * (b + 3);

t1」という中間信号を定義し、「a」に「b + 3」を掛けた結果を格納します。

signal t2; t2 <= eq.out * t1;

t2」という信号に、「eq」の出力に基づいて「t1」の値を格納します。「eq.out」が「1」(つまり「w」が「1」のとき)であれば「t1」が使用されます。

out <= t2 + (1 – eq.out) * (a + b);

最終的な出力「out」は、「eq.out」が「1」のときに「t1」が使用され、「eq.out」が「0」のときに「a + b」が使用されます。これにより、「if-else」の条件が実現されます。

回路をビルド

この実装では、if-else構文のように動的に出力値を変えることができないため、2つの結果を計算してから、それらを合成する形式で条件を表現しています。

circum$./zkall.sh myfunc 12

For文

コードの場所
circom/circuit/sum_up.circom

この回路は、プログラミング言語でのfor文をCircomでどのように表現するかを示しています。具体的には、以下のような関数をCircom回路に変換しています。

signal sums[n+1];

中間的な計算結果を保持するための配列「sums」が定義されています。配列の長さは「n+1」です。

sums[0] <= 0;

配列「sums」の最初の要素を「0」に初期化します。これが累積和を計算する際の基準値となります。

for (var i = 0; i < n; i++) { sums[i+1] <= sums[i] + in[i]; }

ループ内で「sums」の要素に対して累積和を計算しています。「sums[i+1]」には、「sums[i]」に「in[i]」を足した値が格納されます。この処理をn回繰り返します。

out <= sums[n];

最終的に得られた累積和(「sums[n]」)が「out」に出力されます。

component main = SumUp(4);

この回路のメインコンポーネントとして、「SumUp」を長さ「4」の配列に対して適用することを指定しています。

回路をビルド
circum$./zkall.sh sum_up 12

値の範囲チェック

コードの場所
circom/circuit/range_check.circom

この回路は、Circomにおいて特定の値がある範囲内に収まっているかどうかを効率的に確認するためのものです。入力値が設定された範囲内にあるかどうかをチェックし、その結果に応じて「1」または「0」を出力します。このような範囲チェックは、ゼロ知識証明の文脈で、入力が有効であることを確認するために使用されます。

le1.in[0] <= lowerBound; le1.in[1] <= in;

le1」コンポーネントを使って、「lowerBound」と「in」を比較します。

le2.in[0] <= in;le2.in[1] <= upperBound;

le2」コンポーネントを使って、「in」と「upperBound」を比較します。

out <= le1.out * le2.out;

最終的に、「le1」と「le2」の出力(いずれも「0」または「1」)を掛け合わせた結果を「out」として出力します。これにより、「in」が範囲内であれば「1」、範囲外であれば「0」が出力されます。

component main = RangeCheck();

この回路のメインコンポーネントとして、「RangeCheck」を使用することを指定しています。

回路をビルド
circum$./zkall.sh range_check 12

ハッシュ関数: sha256

コードの場所
circom/circuit/hash_sha256.circom

このコードは、SHA-256ハッシュ関数をCircomで実装するためのテンプレートです。SHA-256は広く使われているハッシュ関数であり、暗号学的なデータの整合性確認や署名の一部として頻繁に使用されます。

signal input in[size]; signal output out[256];

「in」はサイズ指定された入力信号で、「out」は256ビットの出力信号です。SHA-256は256ビットのハッシュ値を生成するため、出力が256ビットであることがわかります。

component sha = Sha256(size);

SHA-256ハッシュを計算するコンポーネントを初期化し、sizeで指定された入力サイズを持つようにします。

sha.in <= in;

入力「in」をSHA-256コンポーネントの入力に渡しています。

out <= sha.out;

SHA-256コンポーネントの出力「sha.out」を、このテンプレートの出力「out」として設定しています。

component main = HashSha256(16);

メインコンポーネントとして、入力サイズ「16」に設定されたSHA-256回路を使用しています。

回路をビルド
circum$./zkall.sh hash_sha256 18

CircomでSHA-256を扱う際には、入力も出力もビット配列として処理されるため、ビット単位での演算が必要です。このため、処理はやや複雑で時間がかかります。また、入力がバイト列である場合は、ビット配列に適切に変換する必要があるため、注意が必要です。

ハッシュ関数:mimc

コードの場所
circom/circuit/hash_mimc.circom

MiMCは、ゼロ知識証明(ZKP)に適したハッシュ関数の一つで、SHA-256と比べて処理が高速です。この回路は、MiMCハッシュ関数を用いて、入力値に対応するハッシュ値を計算します。

signal input in;signal output out;

in」は入力信号で、「out」は出力信号です。この回路では、入力値を「MiMC」ハッシュ関数に渡し、その結果を出力します。

component hasher = MiMC7(91);

MiMC7」というハッシュ関数のコンポーネントを初期化しています。「91」という定数は、この「MiMC7」関数のラウンド数などの設定に関連します。

hasher.x_in <= in;

入力「in」をMiMCハッシュ関数の入力「x_in」に渡しています。

hasher.k <= 0;

k」は通常、暗号学的なキーとして使用されますが、この回路ではゼロに設定されています。

out <= hasher.out;

MiMCハッシュ関数の出力「hasher.out」を、このテンプレートの出力「out」として設定しています。

component main = Hash();

メインコンポーネントとして、MiMCハッシュ関数の回路を使用しています。

回路をビルド
circum$ ./zkall.sh hash_mimc 12

このコマンドを実行することで、MiMCハッシュ関数の回路を構築し、指定した引数で検証を行うことができます。

MiMCは、ポセイドン(Poseidon)と並んでZKPの文脈でよく使われるハッシュ関数であり、その主な利点は、ZKPに適した効率的な設計にあります。SHA-256がビット列での処理を必要とするのに対し、MiMCは整数の範囲で動作するため、計算が比較的軽量です。このようにして、ZKPのプロトコルにおける効率的な証明生成と検証が可能になります。

コントラクトウォレットの構築と動作

次に実際に簡易版のコントラクトウォレットの構築と動作を行ってみようと思います。

手順

アプリケーションの構築

ウォレット入りクライアントアプリケーションの作成

目的

ユーザーがEthereumトランザクションを生成し、コントラクトの関数を実行できるアプリケーションを作成します。

手順
  1. ウォレット機能を実装し、ユーザーがEthereumアドレスを管理できるようにします。
  2. Ethereumトランザクション(Tx)を生成する機能を追加します。
  3. コントラクトの関数を呼び出すためのUIを実装します。たとえば、送金指示(どのアドレスに何Eth送るか)を指定できるフォームを作成します。
    Public Input : アドレス
    Private Input : パスワード
    Output : ハッシュ値

Circomでの証明生成

目的

アプリケーションから受け取った入力に基づき、ZK証明を生成します。

手順
  1. Circomをインストールします。
    https://github.com/blockchaininnovation/circompractice.git
  2. Circomを用いて、指定された入力値から証明を生成します。
  3. 生成した証明をアプリケーション内で管理し、コントラクトに送信する準備を整えます。

Circom回路の構築

回路の設計

目的

アドレスやパスワードを入力とし、ハッシュ値を出力する回路を設計します。

手順
  1. Circomファイル内にて、アドレスをPublic Input、パスワードをPrivate Inputとして設定します。
  2. ハッシュ値を計算し、出力します。
  3. 必要に応じて、パスワードの検証ロジックを回路に追加します。
pragma circom 2.0.0;

include "./hash.circom";

template UserAuthentication () {
    signal input useraddress;
    signal input password;
    signal output passwordhash;

    component hash = Hash();
    hash.in <== password;
    passwordhash <== hash.out;
}

component main {public [useraddress]} = UserAuthentication();

このコードは、ユーザーのアドレスとパスワードを受け取り、パスワードのハッシュを計算する回路を定義しています。計算に使用されるハッシュ関数はMiMCで、結果はpasswordhashとして出力されます。ユーザーアドレスはパブリックインプットですが、計算には使用されません。

スマートコントラクトの構築

コントラクトロジックの実装

目的

アプリケーションで生成されたZK証明を検証し、送金やパスワード認証を行うスマートコントラクトを実装します。

手順
  1. ZK検証ロジックをSolidityで実装します。
    ・生成されたZK証明を検証し、トランザクションが有効かどうかを確認します。
  2. アドレスごとの残高管理機能を追加します。
  3. パスワードのハッシュ値で認証する機能を実装します。
pragma solidity ^0.8.19;

contract ContractWallet {
    function registerPasswdHash(uint passwd_hash) public {}

    function deposit() public payable {}

    function transfer(
        address payable send_to, 
        uint amount, 
        uint256[24] calldata _proof, 
        uint256[2] calldata _pubSignals
    ) public {}
}

このコントラクトは、ユーザーが自分のアドレスに関連するパスワードハッシュを登録し、ETHをデポジットし、その後にZKプローフを使用して送金処理を行うことができるウォレットの基本的な機能を提供します。

コマンドの実行

Circom回路生成,検証 用コントラクト出力を行なった後はコントラクトの出力を行うためにコマンドを実行します。このコマンドは、認証回路を生成し、検証用のスマートコントラクト(Verifier.sol)を出力するプロセスを開始します。

circum$ ./zkall.sh auth 12

このコマンドを実行すると、生成されたVerifier.solファイルが、workディレクトリ内に出力されることを確認する必要があります。

デプロイと動作確認

目的

作成したスマートコントラクトをEthereumテストネットにデプロイし、動作を確認します。

手順
  1. コントラクトをテストネットにデプロイします。
  2. アプリケーションから送金指示を行い、コントラクトが正しく動作するかを確認します。
  3. パスワードの認証機能が正しく機能するかどうかを検証します。

Anvilの起動

anvil 」コマンドを使用して、ローカルのブロックチェーンを起動します。これは、コントラクトをデプロイするための環境を整えるステップです。

circum$ anvil

.envファイルの準備:

.env 」ファイルを作成します。具体的には、Contractディレクトリ内にある「.env.sample」ファイルをコピーし、新たに「.env」ファイルとして保存します。

.env 」ファイル内の 「PRIVATE_KEY」 フィールドには、「anvil」 のプロンプトで表示されるアカウントの秘密鍵を入力します。この秘密鍵を使ってコントラクトをデプロイします。

/lesson/circompractice$ cp contract/.env.sample contract/.env

Verifier.solのコピー

Circomで生成された「Verifier.sol」 ファイルを 「contract/src」 内にコピーします。このファイルは、ZK-SNARKs(ゼロ知識証明)を検証するために使用されるスマートコントラクトです。

/lesson/circompractice$ cd contract/
/lesson/circompractice/contract$ cp ../circom/work/auth/Verifier.sol src/

Verifier.solのコード修正

「Verifier.sol」ファイル内のコメントアウトします。これはHardhat用のコードで、Foundryを使用する際にはエラーを引き起こす可能性があるためです。

pragma solidity >=0.7.0 <0.9.0;

// import "hardhat/console.sol";

contract PlonkVerifier {

ビルド

次に、「forge build」コマンドを使って、プロジェクト内のスマートコントラクトをビルドします。これにより、Solidityコードがコンパイルされ、バイナリファイルが生成されます。

# ビルド
$ cd ~/lesson/circompractice/contracts
$ forge build

Anvilへのデプロイ

forge script」コマンドを使用して、Anvilにコントラクトをデプロイします。「--rpc-url」オプションで指定されたローカルホスト上のRPCエンドポイントを使用してデプロイを行います。

# Anvilにデプロイ
$ forge script script/Deployment.s.sol --rpc-url http://127.0.0.1:8545 --broadcast
# デプロイが成功すると、以下のようなコントラクトアドレスが.envファイルに追加されます。
# CONTRACT_WALLET_ADDR=0xe7f1725e7734CE288F8367e1bB143E90bb3F0512

この手順を実行することで、Verifier.solスマートコントラクトをローカルのAnvilブロックチェーンにデプロイし、そのアドレスをアプリケーションで使用できるように設定します。これにより、スマートコントラクトが正常にデプロイされ、次の段階でアプリケーションからコントラクトを実行できる準備が整います。

アプリケーションからコントラクトを実行するための手順

次にアプリケーションからコントラクトを実行するための手順を説明します。

appディレクトリへの移動とモジュールのインストール:

まず、「app」ディレクトリに移動し、必要な「Node.js」のモジュールをインストールします。これにより、アプリケーションが必要とするライブラリがセットアップされます。

# appディレクトリに移動して必要なモジュールをインストール(もう一度app内でインストールが必要)
$ cd ~/git/circompractice/app
$ npm install

config.jsonの編集

次に、「config.sample.json」をコピーして「config.json」という名前のファイルを作成し、それを編集します。このファイルには、ブロックチェーンコントラクトのアドレス秘密鍵などの設定情報が含まれます。これらの情報は、Anvilで出力された秘密鍵と、デプロイ時に生成されたコントラクトアドレスを入力する必要があります。

contractwallet.jsの実行

最後に、アプリケーションのsrcディレクトリ内にある「contractwallet.js」スクリプトを実行します。このスクリプトは、ウォレットの機能を実行し、コントラクトとやり取りを行います。

# contractwallet.jsの実行
$ node src/contractwallet.js

これで、アプリケーションがAnvilブロックチェーン上でコントラクトを実行できるようになります。これにより、スマートコントラクトとのやり取りを行うための基本的なウォレットが構築され、テストネットでのトランザクションなどを実行できるようになります。

ETHをデポジットするプロセス

この手順を実行することで、ウォレットにETHが正しくデポジットされたことが確認でき、次のステップに進む準備が整います。

ETHのデポジット

contractwallet.jsファイルの一番下にコントラクトウォレットに100ETHをデポジットするため、「depositEther("100")」というコード行が使用されます。この部分をコメントアウトから解除して、実行するように設定します。

contractwallet.jsの実行

上記の編集を行った後、「contractwallet.js」を実行します。これにより、100ETHが指定されたウォレットにデポジットされ、その結果が表示されます。実行後にはトランザクションの詳細が表示され、ブロックに確認されることがわかります。

// contractwallet.jsを実行します
$ node src/contractwallet.js

ウォレットの残高をチェックする方法

簡易版コントラクトウォレットの動作確認の一環として、ウォレットの残高をチェックする方法について説明します。この手順を行うことで、ウォレットに正しくETHが追加され、システムが意図した通りに動作していることを確認することができます。

残高チェックの準備

コントラクトウォレットに対してETHが正しくデポジットされたかを確認するため、「getTotalBalance();」という関数を実行します。

このコード行をコメントアウトから解除し、実行することでウォレットの残高を確認できます。

contractwallet.jsの実行

上記のコード行を追加・編集した後、「contractwallet.js」を再度実行します。この際に、ウォレットの現在の残高が表示され、先ほどデポジットしたETHが正しく反映されているかどうかを確認できます。

// contractwallet.jsを実行します
$ node src/contractwallet.js

確認結果

実行結果として、残高が100000000000000000000wei(100ETHに相当)に増加していることが確認できます。また、送金元のアドレスの残高が100ETH減少していることも確認することができます。

// 実行後の出力例
// Balance of 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266: 9599.989076354830614615 ETH
// Balance of 0x7099770c51812dc3a010c7d01b5e0d17dc79c8: 10030.0 ETH
// getTotalBalance() result: 100000000000000000000

認証用のパスワードハッシュを登録する

この手順を行うことで、ZKPに基づく認証システムの一部としてパスワードハッシュの登録が完了し、コントラクトウォレットの残高やトランザクションが正常に動作するかどうかを確認することができます。

パスワードハッシュの登録

入力するハッシュ値は「circom/work/auth/public.jsonのファイルの1番目の値」に対応しているのでpublic.jsonファイルの1番目の値をコピーします。

先ほどコピーしたパスワードハッシュ値を「app/src/contractwallet.js」に戻り、一番したのコメントアウトを外し、パスワードハッシュ値をペーストします。

コントラクトウォレットの実行

// contractwallet.jsを実行します
app$ node src/contractwallet.js

実行すると以下のログが表示されます。

実行結果には、以下の情報が表示されています。

  • ウォレットアドレスの残高
  • パスワードハッシュのトランザクションが送信されたこと
  • トランザクションがブロックに確認されたこと

コントラクトウォレットで送金処理を行う方法

この手順を実行することで、snarkjsによって生成された証明を使った送金処理が正常に行われることを確認できます。手順に通りにコードを実行することで、トランザクションの結果をログに確認することができます。

snarkjsによる証明の使用

「contractwallet.js」ファイル内の「executeTransferByFile(”0x……)」をコメントアウトします。

// 送金処理の実行
executeTransferByFile("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "10", "../circom/work/auth/proof.json", "../circom/work/auth/public.json");

executeTransferByFileに記述されているハッシュ値はAnvilの起動時にcircomフォルダ内で実行した「anvil」のアカウント口座である「Available Accounts」の1番のハッシュ値と同じ値が入ります。

コード内のハッシュ値の次に記述されている「”10″」はsnarkjs によって生成された証明ファイルを使って、特定のアドレスに対して「10ETH」の送金を行うためのものです。

その他のファイルの記述は、証明ファイル 「proof.json」 と公開情報ファイル 「public.json」を使用していることが記されています。

contractwallet.jsの実行

次に上記のコードを用いて、「contractwallet.js 」スクリプトを実行することで、ウォレットの送金処理が行われます。

// contractwallet.jsの実行
app$ node src/contractwallet.js
// 実行結果の例
// Balance of 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: 9599.9897175988053149 ETH
// Balance of 0x70997970C51812dc3A010C7d01b50e0d17dc79C8: 10030.0 ETH
// Transfer transaction sent: 0x8109d31eb6252e4f9370b960e73be59d799e424b6568b8c9c368ba11a91f1f
// Transfer confirmed in block: 26

実行結果には、以下の情報が表示されています。

  • 送信元と送信先のウォレットアドレスの残高。
  • トランザクションが送信されたことの確認。
  • トランザクションがブロックに取り込まれたことの確認。

残高チェック(二回目)

もう一度「getTotalBalance();」のコメントアウトを外して「contractwallet.js」を実行します。

// 実行結果の例
// Balance of 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: 9599.089343021261659418 ETH
// Balance of 0x70997970C51812dc3A010C7d01b50e0d17dc79C8: 10040.0 ETH
// getTotalBalance() result: 90000000000000000000

実行結果は残高が90000000000000000000weiになっていることが確認でき、2番目のアドレスの残高が10ETH増えていることが確認できます。

再度送金を試みる際の手順

新しいプルーフを生成し、それを使用して再度送金処理を試みる手順を行います。エラーメッセージが表示されますが、実行結果として、送金が正常に行われたことが確認でき、これにより、ZKP(Zero-Knowledge Proof)を利用したウォレットの動作が正常に機能していることが確認できます。

もう一度送金を送るための準備を行いコマンドを実行すると送金が失敗し、「shortMessage: ‘transaction execution reverted’」というエラーメッセージが表示されます。

エラーを解決するために以下の動作を行います。

新しいプルーフの生成

コマンド 「./zkprove.sh auth auth」 を実行することで、新しいプルーフを生成します。これは、送金処理を行うための必要な証明を生成するステップです。

~/lesson/circompractice/circom$ ./zkprove.sh auth auth

送金処理の実行

再度送金処理の準備を行います。

コントラクトを実行すると以下のように送金処理が行われたことが確認できます。

Transfer transaction sent: 0xf660618d16bc5f085ab85f9e9221d219d39e898b51fe78d24008a66e1ab54ce78
Transfer confirmed in block: 28

実行結果には、以下の情報が表示されています。

  • 送金トランザクションが送信され、ブロックに正常に確認されたことが表示されます。
  • トランザクションの送信ハッシュやブロック確認の情報がログとして出力されています。

まとめ

以上の手順を実行することで、簡易版コントラクトウォレットを構築し、動作させることができます。これにより、Zero-Knowledge Proofを用いた認証とトランザクション処理を備えた簡易版コントラクトウォレットを構築し、ETHのデポジットと送金が正常に動作することを確認することができましたね。

この事例を参考にして、適切な環境で実装を進めてください。


コメント

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