バンドル構造
証明バンドル(proof bundle)の構成と公開・非公開アーティファクトの区分を解説します。
同期モードと非同期モードで生成されるバンドルの構成、公開許可リスト、および非公開アーティファクト(input.json, verification.json)の保護について説明します。
概要
証明バンドルは、zkVM の実行結果を検証可能な形で保存・配布するためのアーティファクト群です。バンドルには公開可能なファイルと非公開ファイルが含まれ、厳格な許可リストによって第三者に公開するファイルが制御されます。
flowchart TB
subgraph "バンドルディレクトリ"
subgraph "公開アーティファクト"
PI["public-input.json<br/>公開入力"]
RC["receipt.json<br/>STARK レシート"]
JN["journal.json<br/>zkVM ジャーナル"]
MT["metadata.json<br/>メタデータ"]
STH["sth.json<br/>STH スナップショット"]
CP["consistency-proof.json<br/>整合性証明"]
end
subgraph "非公開アーティファクト"
IN["input.json<br/>秘匿入力(ウィットネス)"]
VR["verification.json<br/>検証レポート"]
end
BZ["bundle.zip<br/>公開アーカイブ"]
end
PI --> BZ
RC --> BZ
JN --> BZ
MT --> BZ
STH -.-> BZ
CP -.-> BZ
IN -.-x BZ
VR -.-x BZ
公開許可リスト
バンドルアーカイブ(bundle.zip)に含められるファイルは、許可リストによって厳格に制限されています。
公開可能なアーティファクト
| ファイル | 内容 | 用途 |
|---|---|---|
public-input.json | zkVM に渡された入力の公開可能部分 | 第三者が入力コミットメントを再計算するため |
receipt.json | STARK 証明(Seal)+ ジャーナル + Image ID | 第三者がレシートを独立に検証するため |
journal.json | zkVM ゲストの公開出力(集計結果、除外情報、ビットマップルート等) | 集計結果と整合性データの確認 |
metadata.json | バンドルの作成日時、セッション ID、メソッドバージョン | バンドルの来歴追跡 |
sth.json | 第三者 STH 検証のスナップショット(任意) | STH 合意の再現可能な証拠 |
consistency-proof.json | RFC 6962 整合性証明(任意) | 追記専用性の独立検証 |
sth.json と consistency-proof.json は許可リストに含まれますが、現行フローでは通常生成されません。
非公開アーティファクト
| ファイル | 内容 | 非公開の理由 |
|---|---|---|
input.json | zkVM への完全な入力(全投票データ + Merkle パス) | 投票の秘匿入力(ウィットネス)を含む |
verification.json | 検証サービスの詳細レポート | bundle.zip の許可リスト外(必要時は専用エンドポイントで参照) |
input.json は zkVM ゲストへの完全な入力であり、個々の投票者の選択肢と乱数が含まれます。これが公開されると、投票の秘匿性が失われます。verification.json は bundle.zip の公開配布対象ではなく、必要時のみ専用エンドポイント経由で扱います。
公開入力の構造
public-input.json は、input.json(秘匿入力)から秘密情報を除外した公開版です。
| フィールド | 説明 |
|---|---|
schema | スキーマ識別子("stark-ballot.public_input") |
version | スキーマバージョン("1.0") |
electionId | 選挙 ID(UUID) |
electionConfigHash | 選挙設定のハッシュ |
bulletinRoot | 掲示板の最終ルートハッシュ |
treeSize | 掲示板のツリーサイズ |
totalExpected | 期待される投票数 |
logId | 掲示板ログ ID |
timestamp | 集計時のタイムスタンプ |
methodVersion | zkVM メソッドバージョン |
votes | 各投票のインデックス、コミットメント、Merkle パスの配列 |
votes 配列には各投票のインデックスとコミットメント、Merkle パスが含まれますが、選択肢と乱数は含まれません。これにより、入力コミットメントの再計算が可能でありながら、投票内容の秘匿性は維持されます。
同期バンドルと非同期バンドルの違い
証明生成には同期モード(ローカル実行)と非同期モード(ECS Fargate)の 2 つのパスがあり、生成されるバンドルの構成が異なります。
flowchart LR
subgraph "同期モード"
S1["ローカルプロセスで<br/>ホストバイナリ実行"]
S2["TypeScript で<br/>全アーティファクト生成"]
S3["検証サービスを呼び出し<br/>verification.json を保存"]
S4["許可リストで<br/>bundle.zip を作成"]
S1 --> S2 --> S3 --> S4
end
subgraph "非同期モード"
A1["ECS Fargate で<br/>ホストバイナリ実行"]
A2["entrypoint.sh で<br/>公開アーティファクト生成"]
A3["bundle.zip を<br/>S3 にアップロード"]
A4["コールバック Lambda で<br/>結果をセッションに保存"]
A1 --> A2 --> A3 --> A4
end
| 項目 | 同期モード | 非同期モード |
|---|---|---|
| 実行環境 | ローカルプロセス / Lambda | ECS Fargate コンテナ |
input.json | 生成される(非公開保存) | ワーク入力として S3 に置かれる(公開配布対象外) |
public-input.json | TypeScript で生成 | entrypoint.sh 内で生成 |
journal.json | TypeScript で生成 | *-output.json から bundle.zip 用に生成 |
receipt.json | ホストバイナリ出力を保存 | *-receipt.json を receipt.json としてコピーして同梱 |
metadata.json | 生成される | 生成されない |
verification.json | 検証サービス呼び出し後に保存 | コールバック処理では生成されない |
bundle.zip | 許可リストに基づき作成 | receipt.json / journal.json / public-input.json を同梱して作成 |
| 保存先 | ローカルファイルシステム(+ 任意で S3) | S3 |
| presigned URL | S3 アップロード時に生成 | コールバック Lambda が生成 |
非同期バンドルの生成フロー
非同期モードでは、ECS Fargate コンテナの entrypoint.sh が以下の手順でバンドルを構築します。
- S3 から入力 JSON をダウンロード
- ホストバイナリを実行し、レシートと出力を生成
- 出力から
journal.jsonを変換生成 - 入力と出力から
public-input.jsonを構築 receipt.json、journal.json、public-input.jsonをbundle.zipにアーカイブ*-receipt.json/*-output.json/public-input.json/bundle.zipなどを S3 にアップロード
コールバック Lambda は S3 のバンドルからジャーナルとレシートを取得し、presigned URL を生成してセッションデータに保存します。
非同期モードでは、journal.json は *-output.json から変換され、receipt.json は *-receipt.json をコピーしたものを bundle.zip に格納します。
バンドルディレクトリ構造
同期モード(ローカルファイルシステム)
{VERIFIER_WORK_DIR}/
{sessionId}/
{executionId}/
input.json ← 非公開: ウィットネス
public-input.json ← 公開
journal.json ← 公開
receipt.json ← 公開
metadata.json ← 公開
verification.json ← 非公開: 検証レポート
bundle.zip ← 公開: 許可リストファイルのアーカイブ
非同期モード(S3)
s3://{BUCKET}/sessions/{sessionId}/{executionId}/
input.json ← 非公開: ワーク入力
{inputBase}-receipt.json ← ホストの生出力
{inputBase}-output.json ← ホストの生出力
{inputBase}-journal.json ← ホストが生成した場合のみ
public-input.json ← 公開
bundle.zip ← 公開: 内部は receipt.json / journal.json / public-input.json
verification.json ← `/api/verification/run` 後に追加される場合がある
inputBase はコンテナ実行時に生成される一時入力ファイル名に依存するため、非同期モードの S3 オブジェクト名は固定の receipt.json / journal.json になりません。
バンドルのアクセス方法
ダウンロードエンドポイント
| エンドポイント | 内容 |
|---|---|
GET /api/verification/bundles/:sessionId/:executionId | bundle.zip のダウンロード |
GET /api/verification/bundles/:sessionId/:executionId/report | verification.json の取得 |
S3 にアップロードされたバンドルは presigned URL 経由でアクセスされます。presigned URL は有効期限付きであり、期限切れの場合はクライアントが /api/verify?refreshS3=1 で新しい URL を取得できます。
アーカイブの再現性
同期モード(verification-bundle.ts)の bundle.zip は再現性を確保するため、以下の措置を講じています。
- エントリのタイムスタンプをゼロに固定
- 許可リストに一致するファイルのみを含める
- ファイル名のアルファベット順でエントリを追加
非同期モード(docker/entrypoint.sh)は zip -r で作成され、上記の再現性制御とは実装が異なります。
セキュリティ上の制約
パストラバーサル防止
バンドルのパスセグメント(セッション ID、実行 ID)は英数字とハイフンのみに制限されています。.. を含むパスや許可されていない文字を含むパスは拒否されます。
非公開ファイルの保護
input.json と verification.json は、bundle.zip には含まれません。これらのファイルがバンドルディレクトリに存在していても、許可リストに含まれていないため、アーカイブ作成時に除外されます。