設計と実行フロー
検証パイプラインの設計原則、全体構造、実行フローを解説します。
設計原則
本システムの検証パイプラインは、以下の 3 つの原則に基づいて設計されています。
原則 1: 必要な検証が未実行なら Verified を表示しない
required チェックが not_run(未実行)、pending(依存待ち)、running(実行中)のいずれかにある場合、システムは「Verified」を表示しません。証拠の不在や未解決状態を成功として扱わないという姿勢です。
原則 2: 失敗した検証は即座にブロックする
いずれかの必須チェックが失敗すれば、「Verified」表示は即座にブロックされます。代表的な失敗条件:
excludedSlots > 0(除外されたスロットが存在する)- 整合性証明の失敗
- 公開監査アーティファクトとの不一致
- 第三者 STH 合意の不成立(設定時)
原則 3: チェック評価はサーバー中心、集約はサーバーとクライアントの双方で実施
- 22 個の検証チェックは主にサーバー側(
GET /api/verify)で評価されます。 - Cast-as-Intended のみクライアントがローカルで上書き評価します(サーバーは
not_runを返却)。 - Recorded-as-Cast は cast-time 証跡(
voteReceiptとuserVote.proof)を前提とします。store から再構成できない場合でも/api/verifyは200を返し、関連チェックをnot_runにして全体判定をmissing_evidence側へ fail-closed に倒します。 - STARK 検証は専用サービス(
POST /api/verification/run)で実行され、GET /api/verifyがその結果を読み取ります。 verificationSteps[].statusは required チェック群を基準に導出されますが、現行実装ではjournalやuserVote.proof.treeSizeが不在の場合にverificationSteps[].statusをnot_runに上書きするガード条件もあります。deriveVerificationSummaryはサーバー側の/api/verifyとクライアント側の/verifyの両方で使われます。サーバーはverificationStatusを fail-closed(安全側に倒す方向)に補正し、unsupported な verifier status でもverificationSteps/verificationChecksを含む200応答を返します。クライアントは summary に加えて STARK timeout や transport failure も最終失敗表示に反映します。UI 側の補助分岐については ゲーティングロジック を参照してください。
検証パイプラインの全体構造
1. 段階の依存関係
- Stage 1 — Cast-as-Intended
- Stage 2 — Recorded-as-Cast
- Stage 3 — Counted-as-Recorded
- Stage 4 — STARK Verification
- 結果表示
2. 実行責務(どこで評価・検証されるか)
flowchart TD
subgraph SERVER["サーバー側"]
VFY["GET /api/verify<br/>Stage 2-4 の 22 チェックを組み立て<br/>cast-time 証跡不在時は fail-closed<br/>(Cast は not_run)"]
RUN["POST /api/verification/run<br/>bundle 参照と expected Image ID で<br/>Stage 4 を検証"]
end
subgraph CLIENT["クライアント(UI)"]
UI["Cast のローカル再評価<br/>STARK 解決後に step 表示を開始<br/>summary と明示的 proof failure を反映"]
end
VFY --> UI
RUN --> UI
検証の実行フロー
検証導線では通常 /result から /verify へ進みます。
/resultは継続用のクライアント状態(verificationRequestedAtと正準な finalization snapshot)を保存し、必要ならPOST /api/verification/runを先行起動します/verifyはその継続状態がある場合に検証シーケンスを続行します。STARK が未開始ならシーケンス内で起動できます- 継続状態がなく STARK が
not_runのまま直接/verifyへアクセスした場合は、自動続行せずブロックします /verifyの UI シーケンスは、step を順に見せる前に STARK が terminal status に到達するまでポーリングします。timeout や transport failure は STARK failure として扱われます
sequenceDiagram
participant U as ブラウザ
participant R as /result
participant V as /verify
participant A as API サーバー
participant VS as 検証サービス
U->>R: 「検証へ進む」をクリック
R->>A: POST /api/verification/run(必要時)
A->>VS: bundle 参照 + expected Image ID
VS->>VS: Receipt::verify(imageId)
VS-->>A: 検証レポート保存
R-->>V: /verify へ遷移
loop STARK 完了までポーリング
V->>A: GET /api/verify
A-->>V: 検証ペイロード<br/>(ステップ, チェック, 証明材料)
end
Note over V: 継続には verificationRequestedAt と finalization snapshot が必要<br/>not_run の direct access はブロック<br/>step 表示は STARK 解決後に開始
4 段階の概要
| 段階 | 名称 | 証明する内容 | 検証の実行場所 |
|---|---|---|---|
| Stage 1 | Cast-as-Intended | 投票者の意図通りにコミットメントが生成された | クライアント(/verify 画面でローカル再計算) |
| Stage 2 | Recorded-as-Cast | コミットメントが追記専用掲示板に正しく記録された | サーバー(GET /api/verify) |
| Stage 3 | Counted-as-Recorded | 記録された全投票が正しく集計に含まれた | サーバー(GET /api/verify) |
| Stage 4 | STARK Verification | zkVM の実行が正しく行われたことの暗号学的証明 | サーバー(POST /api/verification/run) |
クライアントは Cast-as-Intended のローカル再計算、STARK 解決後の step 表示、最終表示の整形を担当します。サーバー側の fail-closed 補正と cast-time 証跡不在時の動作は上記「原則 3」を参照してください。
各ステージの verificationSteps[].status は、そのステージで required 扱いになるチェック群から導出されます。たとえば Recorded-as-Cast では、STH ソースが設定されている場合に限り recorded_sth_third_party も必須条件に昇格します。ただし、一部のステージにはデータ不在時のガード条件があります(上記「原則 3」参照)。特に Recorded-as-Cast は userVote.proof.treeSize がなければ not_run になり、Counted-as-Recorded は journal がなければ failed でない限り not_run に補正されます。
各段階の詳細は 4 段階検証モデル を参照してください。
検証チェック数
パイプライン全体で 22 個の検証チェックが定義されており、各チェックには一意の ID が割り当てられています。チェック ID の単一ソースは src/lib/verification/verification-checks.ts です。
| 段階 | チェック数 |
|---|---|
| Cast-as-Intended | 4 |
| Recorded-as-Cast | 6 |
| Counted-as-Recorded | 10 |
| STARK Verification | 2 |
| 合計 | 22 |
Counted-as-Recorded の required チェックには counted_election_manifest_consistent と counted_close_statement_consistent も含まれます。これにより、公開された election-manifest.json / close-statement.json と検証対象データの整合も stage success の条件に含まれます。
チェックの詳細は チェック一覧 を参照してください。