zkVM 設計
RISC Zero zkVM を用いた集計証明パイプラインの全体設計を解説します。
ゲストプログラムが投票データを検証・集計し、ホストが STARK 証明を生成し、検証サービスがレシートを検証する一連のアーキテクチャにより、集計結果の正当性を暗号学的に保証します。
アーキテクチャ概要
zkVM パイプラインは 3 つのコンポーネントで構成されます。
flowchart TB
subgraph C1["1. 入力構築"]
SES[セッションデータ] --> IB[入力ビルダー]
IB --> INP[ZkVMInput]
end
subgraph C2["2. 証明生成"]
INP --> HOST[ホストプログラム]
HOST --> GUEST["ゲストプログラム<br/>(zkVM 内実行)"]
GUEST --> JNL[ジャーナル]
HOST --> RCP[レシート<br/>STARK 証明]
end
subgraph C3["3. 検証"]
RCP --> VS[検証サービス]
VS --> VR[検証レポート]
end
| コンポーネント | 言語 | 責務 |
|---|---|---|
| ゲストプログラム | Rust | zkVM 内で投票を検証・集計し、結果をジャーナルにコミットする |
| ホストプログラム | Rust | 入力を受け取り、zkVM を起動して STARK 証明(レシート)を生成する |
| 検証サービス | Rust | レシートの STARK 検証を実行し、期待される Image ID との一致を確認する |
zkVM とは
RISC Zero zkVM は、RISC-V(RV32IM)命令列として実行されるゲストプログラムに対して、その実行が正しいことを STARK 証明で示すゼロ知識 VM です。
本システムでは「投票検証と集計」をゲストとして実行し、ホストがレシート(seal + journal)を生成します。第三者は Receipt::verify(image_id) により、指定したゲストバイナリ(Image ID)で実行された結果であることを検証できます。
30 秒で要点
| 観点 | 要点 |
|---|---|
| 何を証明するか | 「このゲストコードをこの入力で実行した結果がジャーナルである」こと |
| 何を公開するか | ジャーナル(公開出力)とレシート(証明本体) |
| どこが信頼アンカーか | Image ID(どのゲストを検証対象にするか) |
| この PoC での意味 | 集計値・除外件数・入力整合性を暗号学的に検証可能にする |
RISC Zero zkVM 実装の要点(簡潔版)
RISC Zero の一次情報(zkVM specification / recursion docs)に沿って、実装上重要な点だけを抜粋すると次のとおりです。
- 実行モデル: ゲストは RV32IM として決定論的に実行され、外部との境界は syscall (
ecall) で扱う - 証明の分割: 長い実行はセグメント証明に分割される(大規模実行を扱うため)
- 再帰合成: セグメント証明を再帰的に合成して、最終的に短いレシートへ圧縮する
- 検証 API:
Receipt::verify(image_id)が、証明本体と Image ID バインディングを同時に検証する - 公開出力の束縛:
journalは証明に束縛されるため、検証成功後に改ざんできない
ジャーナルとレシート
- ジャーナル: ゲストが公開出力としてコミットするデータ(検証済み集計、除外情報、入力コミットメントなど)
- レシート: ジャーナルと STARK 証明(seal)のペア。検証成功時、ジャーナルは正しいゲスト実行結果として受理できる
flowchart TB R[レシート] R --> S[Seal<br/>STARK 証明] R --> J[ジャーナル<br/>公開出力]
ジャーナルの各項目は、対応する STARK 証明(レシート検証)により「ゲストプログラムが計算した出力」であることが保証されます。項目ごとの意味と確認ポイントは次のとおりです。
| ジャーナル項目 | 代表フィールド | 主な確認ポイント |
|---|---|---|
| 検証済み集計 | verifiedTally | counted_tally_consistent |
| 除外/欠落情報 | excludedCount など | counted_missing_indices_zero |
| 入力整合性 | inputCommitment | counted_input_commitment_match |
| STH バインディング | sthDigest | recorded_sth_third_party(設定時) |
| 個票包含証明の根 | includedBitmapRoot | counted_my_vote_included |
数学ミニ補足(読み飛ばし可)
STARK の直感的理解に必要な最小限の説明だけを記します。
1. 巡回ドメイン(cyclic domain)
有限体 F の乗法群の部分群 H = {1, w, w^2, ..., w^(n-1)} を評価点集合(ドメイン)として使います。RISC Zero はこれを cyclic domain と呼びます。実行トレース(レジスタ値や遷移)は、この H 上で評価される多項式として扱われます。
2. 有限体上の多項式除算
制約違反の有無は、概念的には次の形で整理できます。
- 制約多項式を
C(x)とする - 評価領域
Hの零化多項式をZ_H(x)とする(典型例:Z_H(x) = x^n - 1) - すべての点で制約を満たすなら
C(h)=0(h in H)となり、C(x)はZ_H(x)で割り切れる - したがって
Q(x) = C(x) / Z_H(x)が多項式として成立するかを検査すれば、制約満足性をチェックできる
実際の STARK では、これを FRI(Fast Reed-Solomon IOP)と Merkle コミットメントを組み合わせて効率的に検証します。
この PoC での保証境界
Receipt::verify(image_id) の成功は強い保証ですが、それ単体で「全票が提示された」ことまでは保証しません。本 PoC では以下を組み合わせて最終判定します。
- STARK 検証成功(正しいゲスト実行)
excludedCount == 0(除外票なし)- 一貫性証明・STH 整合の成功(設定時)
- 必須チェックが
not_run/runningのままでは不合格
このため、Verified 表示は「証明 + 完全性チェック」がそろった場合にのみ成立します。
参考資料(RISC Zero 公式)
データフロー
投票セッションの開始からレシート検証までの全体的なデータフローを示します。
sequenceDiagram participant V as 投票者 participant S as サーバー participant B as 掲示板 participant H as ホスト participant G as ゲスト (zkVM) participant VS as 検証サービス Note over V,B: 投票フェーズ V->>S: 投票コミットメント S->>B: 掲示板に追記 B-->>V: レシート (インデックス, ルート) Note over S: ボット投票を自動追加 Note over S,G: 集計フェーズ S->>S: 入力ビルダーで ZkVMInput を構築 S->>H: ZkVMInput を渡す H->>G: ゲスト実行を開始 G->>G: 各投票のコミットメント検証 G->>G: 各投票の包含証明検証 G->>G: 集計 + ビットマップ計算 G-->>H: ジャーナル出力 H-->>S: レシート (STARK 証明 + ジャーナル) Note over S,VS: 検証フェーズ S->>VS: レシート + 期待 Image ID VS->>VS: Receipt::verify(image_id) VS-->>S: 検証レポート S-->>V: 検証結果を提供
同期モードと非同期モード
ホストプログラムの実行には 2 つのモードがあります。
| 項目 | 同期モード | 非同期モード |
|---|---|---|
| 実行環境 | ローカルプロセス | ECS Fargate タスク |
| 起動方式 | ホストバイナリを直接起動 | SQS → Step Functions → ECS |
| ユースケース | 開発・テスト | 本番環境 |
| タイムアウト | 10 分 | 15 分 |
| 結果の取得 | プロセス終了後に直接読み取り | S3 経由 + コールバック Lambda |
詳細は ホストと証明生成 を参照してください。
開発モードと本番モード
RISC Zero は RISC0_DEV_MODE 環境変数により、開発モードと本番モードを切り替えます。
| 項目 | 開発モード (RISC0_DEV_MODE=1) | 本番モード |
|---|---|---|
| 証明の種類 | フェイクレシート | 本物の STARK 証明 |
| 実行時間 | 約 100 ミリ秒 | 約 370 秒(64 票の場合) |
| 安全性 | なし(検証を省略) | 暗号学的に完全 |
| 検証サービス | DevMode として検出 | 完全な STARK 検証を実行 |
開発モードで生成されたレシートは InnerReceipt::Fake 型となります。検証サービスでは通常 DevMode として扱いますが、image_id 不一致などの事前条件違反があれば Failed になります。本番環境でフェイクレシートが混入した場合は検証失敗です。