RISC Zero v1系でzk-STARKによるRustプログラムの証明と検証をする

お知らせ
Table of Contents

はじめに

zk-STARKを使ったプログラム検証フレームワークであるRISC Zero1の概要について、以前このブログで紹介しました2
今回は実際に証明プログラムをコンパイルして動作させるところまでやってみたいと思います。

RISC Zeroがどういうものなのかを知りたい方は以前の記事2をまず読んでみてください。

検証方法

運の悪いことに、この記事を書いている時点ではv2系が出る間近ですが、v1.2.5系を使います。将来この記事が役に立つかは不明です。互換性がないのでインストールするプログラムとソースコードのバージョンは一致させる必要があります。

file

インストール

以下に従ってインストールします。
https://dev.risczero.com/api/zkvm/install

ソースコードをclone

https://github.com/risc0/risc0 からソースコードを持ってきます。
今回は git checkout v1.2.5v1.2.5 ( 2225069ebc465320695ef3d5e028f6072e103718 )を使っています。

hello-worldを実行

hello worldプログラム3が用意されているので、それを実行してみます。
検証する内容としては、READMEに書いてありますが 391 の因数分解です。証明者は391の因数を検証者に教えずに、因数を知っているということだけを証明します。

cd examples/hello-world && cargo run --release で実行すると内部で証明の生成と検証が行われます。

編集

examples/hello-world/src/main.rs を編集

上記のexampleでは、証明ファイル(receipt)が生成されないので、本当に検証できているのかがわかりません。
どのような証明ファイルが生成されているのかを確認するために、base64エンコードしたファイルを出力してみます。

use base64::{engine::general_purpose, Engine as _};
use hello_world::multiply;
use hello_world_methods::MULTIPLY_ID;
use risc0_zkvm::Receipt;

fn main() {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    // Pick two numbers
    let (receipt, _) = multiply(17, 23);

    // Here is where one would send 'receipt' over the network...
    let encoded = borsh::to_vec(&receipt).unwrap();
    let b64encoded = general_purpose::STANDARD.encode(&encoded);
    println!("Encoded receipt: {:?}", b64encoded);
    let b64decoded = general_purpose::STANDARD.decode(&b64encoded).unwrap();
    let decoded: Receipt = borsh::from_slice(&b64decoded).unwrap();

    // Verify receipt, panic if it's wrong
    decoded.verify(MULTIPLY_ID).expect(
        "Code you have proven should successfully verify; did you specify the correct image ID?",
    );
}

examples/hello-world/Cargo.toml を編集

receiptオブジェクトをVec<u8>にエンコードするためにborshを使い、さらにそこからbase64文字列にエンコードしています。

[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"

[dependencies]
hello-world-methods = { path = "methods" }
risc0-zkvm = { path = "../../risc0/zkvm" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
borsh = { version = "1.5", default-features = false, features = ["derive"] }
base64 = "0.21"

[features]
cuda = ["risc0-zkvm/cuda"]
default = []
prove = ["risc0-zkvm/prove"]

再度実行

cd examples/hello-world && cargo run --release で実行すると内部で証明の生成と検証が行われます。標準出力に巨大なbase64データが出てきます。サイズを確認すると215KBくらいありました。

中身を見てみる

risc0/build/README.md に概要が書いてあります。内部では以下のようなことをやっています。

  1. ゲストプログラムである examples/hello-world/methods/guest/src/main.rs がRISC V向けにコンパイルされる。no_std, no_mainフラグが付いていることから分かるように、これは仮想マシン(RISC-V)上で実行されるプログラムである。
  2. ゲストプログラムのコンパイル先は examples/target/riscv-guest/hello-world-methods/multiply/riscv32im-risc0-zkvm-elf/release/multiply になる。このサイズは 81KBくらいだった。
  3. ゲストプログラムのハッシュ値とプログラム本体をrustから利用できるようにするための中間ファイルが生成される。このパスがどう生成されているのかは不明。 examples/target/debug/build/hello-world-methods-*/out/methods.rs中身は以下の通りで、ID値とプログラム本体のパスを利用できるようにしている。
pub const MULTIPLY_ELF: &[u8] = include_bytes!("****/examples/target/riscv-guest/hello-world-methods/multiply/riscv32im-risc0-zkvm-elf/release/multiply");
pub const MULTIPLY_ID: [u32; 8] = [3639933153, 2839713648, 2475961461, 2271298785, 685092703, 3270430304, 4042366863, 1503306767];
pub const MULTIPLY_PATH: &str = "****/examples/target/riscv-guest/hello-world-methods/multiply/riscv32im-risc0-zkvm-elf/release/multiply";
  1. 上記のゲストプログラムを利用して、examples/hello-world/src/main.rs から呼ばれた examples/hello-world/src/lib.rs の中で証明生成が行われる。結果として215KB程度のReceipt(証明)が生成される。
  2. ReceiptとゲストプログラムのIDを利用して examples/hello-world/src/main.rs にて証明が検証される。検証側はプログラム本体を持っていなくても、プログラムのIDだけがあれば検証が可能になる。
  3. Receipt内のjournalからは計算結果である 391 という数字を見ることができるので、検証者は391という積は知っているが、その因数は知らないことになる。

感想

  • 証明の対象は単純なプログラムですが、コンパイルしたサイズは81KBと大きいです。RISC Zeroのゲスト環境ライブラリ(zk-STARKをzk-SNARKでラップしている等)のオーバーヘッドが大きい可能性が考えられます。また、ゲスト環境を統一することで(例えば、ゲスト環境が必ずプログラムのIDを検証するようなコードを持つことで)検証者はプログラム本体を持っていなくても検証ができるようになっているので、トレードオフとしては問題ないと感じました。
  • cairoと違いrustで直接RISC-Vにコンパイルできるのは学習コスト的にかなり楽です。あと検証者側も当然独立してプログラムのコンパイルができるので、cairoよりも現状安全な選択肢だと思います。
  • 複雑なプログラムを与えたときに証明サイズやプログラムサイズがどのように変化するのかは確認する必要があると思っています。

参考文献


  1. RISC Zero is a zero-knowledge verifiable general computing platform based on zk-STARKs and the RISC-V microarchitecture. https://github.com/risc0/risc0 

  2. STARKを用いてRISC-Vの実行履歴証明を行うフレームワークRISC Zeroの紹介 https://tech.hashport.io/4512/ 

  3. Hello World for the RISC Zero zkVM https://github.com/risc0/risc0/blob/d147f436fec2a8469ed378fc939784e64115aaa9/examples/hello-world/README.md 

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