Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

チェック一覧

全検証チェック ID の定義、判定ロジック、失敗時の影響を一覧で解説します。

各チェックは一意の ID を持ち、success / failed / not_run / running / pending のステータスで管理されます。not_run のチェックが残っている場合、「Verified」は表示されません。

チェックの属性

各チェックには以下の属性が定義されています。

属性説明
IDチェックの一意な識別子(スネークケース)
カテゴリ所属する検証段階
証拠種別チェックに使用するデータの出所
重要度required(必須)または optional(任意)
派生元他のチェックから結果を導出する場合のソースチェック ID

証拠種別

種別説明
local投票者の端末に保持されたユーザー固有データ(localStorage の投票意図など)
public掲示板や capability 保護 API から取得する、秘密データを含まない検証用データ
zkzkVM が束縛した公開証拠(ジャーナル、receipt 検証結果、bitmap root に基づく証明など)

重要度

重要度説明
required「Verified」表示に必須。失敗すれば即座にブロック
optional補助的な検証。通常は単独で失敗扱いにしないが、実行時条件により blocking に昇格する場合がある

代表例は recorded_sth_third_party です。

注意: verificationChecks と UI 表示用の verificationSteps は 1 対 1 ではありません。対応関係は ゲーティングロジック を参照してください。


Cast-as-Intended(4 チェック)

投票者の意図通りにコミットメントが生成されたかを検証するチェック群です。 このカテゴリはクライアントが voteReceipt/api/verify 応答)とローカル投票意図(electionId/myVote/myRand)を使って評価します。

ID説明証拠種別重要度
cast_receipt_present投票レシートが存在し、voteId とコミットメントを含むlocalrequired
cast_choice_range選択肢が有効範囲内(A〜E)localrequired
cast_random_format乱数が 32 バイトの 16 進数文字列localrequired
cast_commitment_match投票時データから再計算したコミットメントが投票レシートと一致localrequired

判定ロジックの詳細

cast_receipt_present

voteReceipt が存在し、voteIdcommitment フィールドが存在することを確認します(このチェック単体では UUID/hex 形式までは検証しません)。

cast_choice_range

投票時データの選択肢が AE のいずれかであることを確認します。範囲外の値は不正な入力として failed となります。

cast_random_format

投票時データの乱数が 32 バイト(64 文字)の 16 進数文字列であることを確認します。0x プレフィックスの有無は正規化により吸収されます。

cast_commitment_match

投票時データ(選挙 ID、選択肢、乱数)からコミットメントを再計算し、投票レシートのコミットメント値と照合します。ドメインタグ "stark-ballot:commit|v1.0" を含む正準フォーマットに従います。


Recorded-as-Cast(6 チェック)

コミットメントが掲示板に正しく記録されたかを検証するチェック群です。

ID説明証拠種別重要度
recorded_commitment_in_bulletinコミットメントが掲示板ツリーに存在するpublicoptional
recorded_index_in_range掲示板インデックスが 0 以上かつツリーサイズ未満publicrequired
recorded_root_at_cast_consistent投票時のルートが最終ツリーの正当なプレフィックスであるpublicoptional
recorded_inclusion_proofRFC 6962 包含証明が暗号学的に検証成功publicrequired
recorded_consistency_proofRFC 6962 整合性証明が暗号学的に検証成功publicrequired
recorded_sth_third_party第三者 STH ソース間で合意が成立(比較可能応答間)publicoptional

判定ロジックの詳細

recorded_commitment_in_bulletin

包含証明(recorded_inclusion_proof)の結果から派生します。包含証明が成功すれば、コミットメントがツリーに存在することが暗号学的に証明されています。

recorded_index_in_range

掲示板インデックスが 0 <= index < treeSize の範囲内であることを確認します。範囲外のインデックスは、データの不整合を示します。

recorded_root_at_cast_consistent

整合性証明(recorded_consistency_proof)の結果から派生します。整合性証明が成功すれば、投票時のルートが最終ツリーの有効な追記専用プレフィックスであることが証明されています。

recorded_inclusion_proof

投票者のコミットメントに対する RFC 6962 包含証明(監査パス)を検証します。cast-time 証跡voteReceiptuserVote.proof)が揃っている場合、まず cast snapshot の一貫性(leafIndextreeSizebulletinRootAtCast が receipt と矛盾しないこと)を確認したうえで、リーフハッシュと監査パスから cast 時点のルートを再計算して照合します。証跡が揃わない場合は not_run となり、全体判定は fail-closed で missing_evidence 側へ倒れます。

recorded_consistency_proof

投票時のツリー(oldSize, oldRoot)から最終ツリー(newSize, newRoot)への RFC 6962 整合性証明を検証します。cast-time 証跡を前提に、まず cast snapshot の一貫性(leafIndextreeSizebulletinRootAtCast)を receipt と照合し、さらに bulletin provider から取得した old/new 両時点の root が期待値と一致することを確認します。これにより、投票時ルートが最終ツリーの追記専用プレフィックスであることを保証します。証跡が欠ける場合は not_run となり、全体判定は fail-closed に扱われます。

recorded_sth_third_party

設定された STH ソースからスナップショットを取得し、比較可能な応答同士で合意を確認します。判定は matchingSources >= minMatches(デフォルト: 2)に加えて、比較対象になった応答間の全会一致(consensus)が必要です。照合対象は STH ダイジェストが必須で、bulletinRoot / treeSize は各ソースが返した場合に追加で照合されます。STH ソースが未設定の場合は not_run になります。

このチェック定義の criticalityoptional ですが、サマリー判定では STH ソースが設定されている場合に実質的な必須条件として扱われます。

派生チェックについて

recorded_commitment_in_bulletinrecorded_root_at_cast_consistent は、それぞれ recorded_inclusion_proofrecorded_consistency_proof から結果を派生します。これは、包含証明の検証成功がそのまま「コミットメントの存在」の証明となり、整合性証明の検証成功がそのまま「ルートの一貫性」の証明となるためです。

派生チェックの重要度が optional であるのは、派生元の required チェックが既にカバーしているためです。


Counted-as-Recorded(10 チェック)

記録された全投票が正しく集計に含まれたかを検証するチェック群です。

ID説明証拠種別重要度
counted_input_sanity公開入力サマリーが有効publicrequired
counted_unique_indices入力中の全インデックスが一意publicrequired
counted_unique_commitments入力中の全コミットメントが一意publicrequired
counted_tally_consistentclaimed tally と zkVM の検証済み集計が一致(fallback: 合計整合)zkrequired
counted_missing_indices_zero解決済み fail-closed exclusion count(excludedSlots 優先)が 0zkrequired
counted_expected_vs_tree_sizetotalExpected がツリーサイズと一致zkrequired
counted_election_manifest_consistentelection-manifest.json が自己整合し、選挙 ID / electionConfigHash と一致publicrequired
counted_close_statement_consistentclose-statement.json が自己整合し、log/tree/timestamp/root/sthDigest と一致publicrequired
counted_my_vote_includedbitmap 証明により自分のインデックスが counted 側に含まれたことを確認zkrequired
counted_input_commitment_match公開入力から計算した入力コミットメントがジャーナルの値と一致publicrequired

判定ロジックの詳細

counted_input_sanity

public-input.json 相当から組み立てた公開入力サマリーが存在し、スキーマ検証に成功していることを確認します。加えて、treeSize が正の整数、votesCount <= treeSizebulletinRoot が 32 バイト hex かつゼロ値でないことを要求します。

counted_unique_indices

zkVM に渡された全投票のインデックスが重複なく一意であることを確認します。重複インデックスは、同一投票の二重カウントを示す可能性があります。

counted_unique_commitments

zkVM に渡された全投票のコミットメントが重複なく一意であることを確認します。重複コミットメントは、同一コミットメントに対する複数投票を示す可能性があります。

counted_tally_consistent

主経路では、公開される claimed tally(tally.counts)が zkVM の verifiedTally と選択肢ごとに一致し、かつ verifiedTally の合計が tally.totalVotes と一致することを確認します。これにより「公開表示された集計値」と「zkVM が証明した集計値」の不一致を検出します。claimed tally が利用できない場合は、フォールバックとして journal.verifiedTally の合計と journal.validVotes の一致を検証します。

counted_missing_indices_zero

fail-closed(安全側に倒す)な除外数が 0 であることを確認します。除外数は以下の優先順で解決します。

  1. journal がある場合: journal.excludedSlots を使用し、必要に応じて旧フィールドへの互換値も導出
  2. journal がない場合: excludedSlotsexcludedCountmissingSlots + invalidPresentedSlotsmissingIndices + invalidIndices の順で探索

rejectedRecords は説明用の補助値であり、この判定には使いません。解決値が 0 でない場合、または journal 側の値が無効な場合は即座に検証失敗とします。

counted_expected_vs_tree_size

totalExpected(期待される投票数)が掲示板のツリーサイズと一致することを確認します。不一致は、暗黙の投票除外を示す可能性があります。

counted_election_manifest_consistent

election-manifest.jsonelectionConfigHash を再計算し、manifest 自身の宣言値と一致することを確認します。そのうえで electionIdelectionConfigHash を、セッション値・publicInputSummary・ジャーナルに含まれる対応値と相互照合します。

counted_close_statement_consistent

close-statement.json から sthDigest を再計算し、宣言された snapshot と一致することを確認します。そのうえで timestamppublicInputSummary.timestamp と一致し、logId / treeSize / bulletinRoot / sthDigest が検証入力やジャーナルと矛盾しないことを確認します。

counted_my_vote_included

includedBitmapRoot に対する bitmap Merkle 証明を検証し、投票者のインデックスに対応するビットが 1(counted に含まれた)であることを確認します。

seenBitmapRoot もある場合は /api/bitmap-proof?kind=included|seen の両方を使い、presented but invalid / not presented to the prover / unknown excluded を説明可能にします。

証明材料が取得できない場合は not_run になり、補助 note が付くことがあります。

counted_input_commitment_match

公開入力から再構成した入力コミットメント対象フィールドの正準ハッシュを計算し、zkVM ジャーナルに記録された入力コミットメント値と照合します。現行実装で対象となるのは electionIdbulletinRoottreeSizetotalExpectedvotesCount と、各投票の indexcommitmentmerklePath です。これは public-input.json 全体の単純なハッシュではありません。


STARK Verification(2 チェック)

STARK 証明の暗号学的正当性を検証するチェック群です。

ID説明証拠種別重要度
stark_image_id_matchverifier-confirmed な Image ID と expected / host-side metadata が整合zkrequired
stark_receipt_verifySTARK レシートが暗号学的に検証成功zkrequired

判定ロジックの詳細

stark_image_id_match

verifier-service が返す expected_image_idreceipt_image_id が一致することを確認します。

加えて、以下の補助値が存在する場合は相互矛盾がないことも検証します。

  • imageId(ホスト主張値)
  • journal.imageId(比較用メタデータ)

いずれかが receipt_image_id と食い違う場合は失敗となります。

期待 Image ID の解決順:

  1. EXPECTED_IMAGE_ID 環境変数
  2. public/imageId-mapping.json の current mapping(WSL では expectedImageID_x86_64 が自動選択される場合あり)
  3. 組み込み fallback(mapping を読めない場合のみ)

stark_receipt_verify

サーバー側の Rust 検証サービスが Receipt::verify(image_id) を実行し、Seal(STARK 証明)が暗号学的に正当であることを確認します。チェック結果としては success / failed / not_run / running が使われ、dev_mode は許可設定に応じて success または not_run に正規化されます。


チェックステータスの遷移

stateDiagram-v2
  [*] --> not_run: 初期状態
  not_run --> pending: 依存条件の待機
  not_run --> running: 検証開始
  pending --> running: 依存条件の解消
  running --> success: 検証成功
  running --> failed: 検証失敗
ステータス説明「Verified」への影響
not_run関連データが未取得、または検証未開始ブロック
pending依存する検証の完了を待機中ブロック
running検証を実行中ブロック
success検証成功許可
failed検証失敗ブロック

全チェック早見表

#チェック IDカテゴリ証拠重要度派生元
1cast_receipt_presentCastlocalrequired
2cast_choice_rangeCastlocalrequired
3cast_random_formatCastlocalrequired
4cast_commitment_matchCastlocalrequired
5recorded_commitment_in_bulletinRecordedpublicoptionalrecorded_inclusion_proof
6recorded_index_in_rangeRecordedpublicrequired
7recorded_root_at_cast_consistentRecordedpublicoptionalrecorded_consistency_proof
8recorded_inclusion_proofRecordedpublicrequired
9recorded_consistency_proofRecordedpublicrequired
10recorded_sth_third_partyRecordedpublicoptional
11counted_input_sanityCountedpublicrequired
12counted_unique_indicesCountedpublicrequired
13counted_unique_commitmentsCountedpublicrequired
14counted_tally_consistentCountedzkrequired
15counted_missing_indices_zeroCountedzkrequired
16counted_expected_vs_tree_sizeCountedzkrequired
17counted_election_manifest_consistentCountedpublicrequired
18counted_close_statement_consistentCountedpublicrequired
19counted_my_vote_includedCountedzkrequired
20counted_input_commitment_matchCountedpublicrequired
21stark_image_id_matchSTARKzkrequired
22stark_receipt_verifySTARKzkrequired