検証サービス
Rust ベースの STARK レシート検証サービスの設計を解説します。
Receipt::verify(expected_image_id) により、レシートが期待されるゲストプログラムの実行結果であることをサーバー側で検証します。検証結果はレポートとして永続化され、クライアントに提供されます。
概要
検証サービスは、ホストプログラムが生成したレシートの STARK 検証をサーバー側で行う Rust ベースのコンポーネントです。
STARK 証明の検証は計算コストが証明生成に比べて低いものの、ブラウザ上で RISC Zero の検証ロジックを実行することは現時点では実用的ではありません。そのため、本システムではサーバー側で検証を行い、その結果をレポートとしてクライアントに提供する設計を採用しています。
flowchart LR
subgraph 入力
RCP[レシート JSON]
EID["期待 Image ID"]
end
RCP --> VS[検証サービス]
EID --> VS
VS --> RPT[検証レポート]
検証フロー
検証サービスは以下の手順でレシートを検証します。
flowchart TD
START[レシート読み込み] --> PARSE[JSON パース]
PARSE --> FORMAT{レシート形式<br/>の判定}
FORMAT -->|フラット JSON| F1[直接パース]
FORMAT -->|ネスト JSON| F2[receipt フィールドを抽出]
FORMAT -->|ZIP アーカイブ| F3[末尾が receipt.json のファイルを探索]
F1 --> EXTRACT[Image ID 抽出]
F2 --> EXTRACT
F3 --> EXTRACT
EXTRACT --> MODE[InnerReceipt を判定<br/>Fake は dev_mode 候補として記録]
MODE --> MATCH{メタデータ Image ID<br/>= 期待 Image ID ?}
MATCH -->|不一致| FAIL1[検証失敗:<br/>Image ID 不一致]
MATCH -->|一致| VERIFY["Receipt::verify()<br/>STARK 検証実行"]
VERIFY -->|成功 かつ Fake| DEVMODE[dev_mode]
VERIFY -->|失敗 かつ Fake + InvalidProof| DEVMODE
VERIFY -->|成功 かつ 非 Fake| SUCCESS[success]
VERIFY -->|失敗(その他)| FAIL2[failed]
1. レシートの読み込みとパース
検証サービスは複数のレシート形式に対応しています。
| 形式 | 説明 |
|---|---|
| フラット JSON | レシートオブジェクトが直接 JSON のトップレベルにある |
| ネスト JSON | { "receipt": {...}, "image_id": "0x..." } 構造 |
| ZIP アーカイブ | エントリ名の末尾が receipt.json のファイルを探索 |
2. 開発モードの検出
レシートの内部構造(InnerReceipt)がフェイク(Fake)型の場合、開発モードで生成されたレシートとして扱います。開発モードのレシートは本物の STARK 証明としては成立しません。
ただし、Fake 検出だけで即 dev_mode にはなりません。Image ID メタデータが不一致の場合は failed となります。照合後の Receipt::verify() で Fake レシートが InvalidProof を返したケースは dev_mode として分類します。
補足として、image_id メタデータが欠落している場合は、非 Fake レシートでは failed、Fake レシートでは Receipt::verify() の結果(通常 InvalidProof)に基づいて dev_mode になる経路があります。
3. Image ID の照合
レシート JSON のメタデータ image_id を期待値と照合します。不一致は failed として即時拒否され、Receipt::verify() には進みません。
この事前チェックにより、異なるゲストプログラムで生成されたレシートを早期に拒否できます。Image ID の管理については Image ID を参照してください。
4. STARK 検証の実行
Image ID の照合に成功した後、RISC Zero SDK の Receipt::verify() メソッドを使用して STARK 証明の暗号学的検証を行います。
この検証は以下のことを証明します:
- レシートに含まれる seal(証明データ)が有効である
- ジャーナルの内容が、指定された Image ID のゲストプログラムの正当な実行結果である
- 証明の生成時にデータの改ざんが行われていない
検証レポート
検証サービスは検証結果を構造化されたレポートとして出力します。
| フィールド | 型 | 説明 |
|---|---|---|
| status | 列挙型 | success / failed / dev_mode |
| expected_image_id | 文字列 | 検証に使用した期待 Image ID |
| receipt_image_id | 文字列? | レシートから抽出した Image ID(null 可) |
| dev_mode_receipt | 真偽値 | フェイクレシートであるか |
| errors | 文字列[] | 失敗時の診断情報の配列 |
ステータスの判定基準
flowchart TD
S{検証結果}
S -->|"metadata image_id 不一致"| F1["failed<br/>errors: image_id_mismatch"]
S -->|"Receipt::verify 成功 + 非 Fake"| OK["success"]
S -->|"Receipt::verify 成功 + Fake"| DM["dev_mode"]
S -->|"Receipt::verify 失敗 + Fake + InvalidProof"| DM2["dev_mode"]
S -->|"Receipt::verify 失敗(その他)"| F2["failed<br/>errors: verification_failed"]
| ステータス | 意味 | UI への影響 |
|---|---|---|
success | STARK 検証が成功し、Image ID も一致 | STARK Verified を表示可能 |
failed | Image ID 不一致または STARK 検証失敗 | 検証失敗として表示 |
dev_mode | 開発モードのフェイクレシート | 開発モード警告を表示 |
デプロイメントモデル
検証サービス(Rust バイナリ verifier-service)は、呼び出し経路ごとに実行場所が異なります。
- 同期ファイナライズ(
POST /api/finalize): real executor 利用時のみ API サーバープロセスがローカルに配置されたバイナリを直接実行(mock executor 利用時は verifier-service を呼ばずdev_mode扱い) - 明示的検証(
POST /api/verification/run): verifier-service-runner Lambda が S3 バンドルを展開してバイナリを実行
sequenceDiagram participant C as クライアント participant API as API サーバー participant RUNNER as verifier-service-runner Lambda participant VS as verifier-service バイナリ participant S3 as S3 C->>API: POST /api/verification/run API->>RUNNER: 実行要求(sessionId, executionId, expectedImageId) RUNNER->>S3: レシートバンドルを取得・展開 RUNNER->>VS: レシート + 期待 Image ID VS->>VS: STARK 検証実行 VS-->>RUNNER: 検証レポート RUNNER-->>API: 検証レポート API->>API: verification.json として保存 API-->>C: 検証結果
呼び出しパターン
検証サービスの呼び出しには 2 つのパターンがあります。
| パターン | トリガー | 説明 |
|---|---|---|
| 同期実行 | 同期ファイナライズ (POST /api/finalize) | real executor 時のみ、サーバー内の proof-bundle 処理で verifier-service を実行 |
| 明示的実行 | クライアントが検証を要求 | POST /api/verification/run 経由で実行 |
非同期ファイナライズのコールバック Lambda は、結果の復元と保存を担当します。STARK 検証は自動実行されず、POST /api/verification/run で実行します。
検証パイプラインにおける役割
検証サービスは、4 段階検証モデルの最終段階である STARK 検証を担当します。
| チェック ID | 検証内容 |
|---|---|
stark_image_id_match | レシートの Image ID が期待されるゲストプログラムの ID と一致するか |
stark_receipt_verify | STARK 証明が暗号学的に有効であるか |
これらのチェックが両方成功した場合に限り、「STARK Verified」のステータスが付与されます。詳細は 4 段階検証モデル を参照してください。
セキュリティ上の考慮事項
サーバー側検証の信頼境界
検証サービスはサーバー側で実行されるため、クライアントはサーバーの検証結果を信頼する必要があります。この PoC における信頼モデルは以下の通りです:
- STARK 証明自体は公開データ: レシートと Image ID が公開されていれば、第三者が独立に検証可能
- 検証サービスは利便性のための委任: ブラウザでの STARK 検証が実用的になれば、クライアント側検証に移行可能
- 公開バンドル: レシートと公開入力は ZIP ローカル検証(Ubuntu) の手順で独立検証できる
verification.json の非公開性
verification.json は公開バンドル(bundle.zip)には含まれません。必要に応じてレポート用エンドポイント経由で配布されますが、第三者検証ではレシートファイルを直接使った独立検証が推奨されます。