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

非同期プローバー

SQS → Step Functions → ECS Fargate による非同期証明パイプラインの設計を解説します。

STARK 証明の生成は 64 票で約 6 分を要するため、同期的な HTTP リクエスト内では完了できません。非同期パイプラインにより、Web リクエストのタイムアウトを回避しつつ、高負荷な証明生成を安全に実行します。

この章は FINALIZE_ASYNC_MODE=true で動作する非同期経路を対象としています。

パイプライン全体像

flowchart TB
  API["POST /api/finalize"] --> SQS["SQS<br/>ワークキュー"]
  SQS --> DP["prover-dispatch-proxy<br/>Lambda"]
  DP --> S3U["S3<br/>input.json 保存"]
  DP --> SFN["Step Functions<br/>StartExecution"]
  SFN --> SIG["check-image-signature<br/>Lambda"]
  SIG --> CHK{"署名 COMPLETE?"}
  CHK -->|Yes| ECS["ECS Fargate<br/>RunTask"]
  CHK -->|No| SFNFAIL["Step Functions<br/>署名失敗分岐"]
  ECS --> S3W["S3<br/>bundle / sibling artifacts 保存"]
  ECS --> SFNRET["Step Functions<br/>成功/失敗を判定"]
  SFNFAIL --> CB["finalize-callback-runner<br/>Lambda"]
  SFNRET --> CB
  CB --> DDB["AppSync/DynamoDB<br/>セッション更新"]

詳細な失敗分岐(署名検証 NG、プローバー実行失敗)は、後続のステートマシン図で示します。

各フェーズの詳細

フェーズ 1: リクエスト受付

クライアントが POST /api/finalize を呼び出すと、API ハンドラーは以下の処理を行います。

以下は FINALIZE_ASYNC_MODE=true の場合です。

  1. セッションの状態を検証(全投票が完了していること)
  2. zkVM 入力を構築(入力ビルダーが投票データ + Merkle パスを組み立て)
  3. セッションの finalizationState を「pending」に更新
  4. SQS キューにメッセージを送信
  5. クライアントに 202 Accepted を返却

クライアントはその後、GET /api/sessions/:id/status をポーリングして進捗を確認します。

フェーズ 2: ディスパッチ

prover-dispatch-proxy Lambda が SQS メッセージを受信し、以下を実行します。

  1. zkVM 入力 JSON を S3 にアップロード(sessions/{sessionId}/{executionId}/input.json
  2. Step Functions のステートマシンを StartExecution で起動
  3. セッションの finalizationState を「running」に更新し、executionId を記録

注: S3 パスプレフィックスは Terraform 変数 s3_proof_prefix で変更可能です。ただし Amplify 側の環境変数・一部 IAM/CLI 補助ポリシー・verifier-service-runner が sessions/ を前提としているため、変更時はそれらも合わせて更新が必要です。

フェーズ 3: 証明生成

Step Functions ステートマシンが 3 つのステップを順次実行します。

stateDiagram-v2
  [*] --> VerifyImageSignature
  VerifyImageSignature --> CheckImageSignature
  CheckImageSignature --> RunProver: 署名 COMPLETE
  CheckImageSignature --> FinalizeSignatureFailed: 署名 NG
  RunProver --> FinalizeSucceeded: 成功
  RunProver --> FinalizeFailed: 失敗
  FinalizeSignatureFailed --> [*]
  FinalizeSucceeded --> [*]
  FinalizeFailed --> [*]

VerifyImageSignature

check-image-signature Lambda を呼び出し、ECR イメージのダイジェストに対する AWS Signer 署名のステータスを確認します。詳細は イメージ署名 を参照してください。

CheckImageSignature

Choice ステートで署名ステータスを判定します。COMPLETE であれば RunProver に進み、それ以外は FinalizeSignatureFailed に遷移してコールバック Lambda に失敗を通知します。

RunProver

ECS Fargate タスクを ecs:runTask.sync(同期モード)で起動します。Step Functions はタスクの完了を待機し、成功/失敗に応じて対応するコールバックステートに遷移します。

現行の Terraform 実装には TIMED_OUT 専用ステートはなく、Step Functions の終端は FinalizeSucceeded / FinalizeFailed / FinalizeSignatureFailed の 3 つです。callback Lambda は将来の拡張に備えて TIMED_OUT も受理できますが、現行の State Machine からは送出されません。

ECS タスクのコンテナには、Step Functions の入力からセッション固有の環境変数が注入されます。

環境変数値の由来説明
ENV_NAMETerraform 変数環境名(develop / main)
S3_PROOF_BUCKETTerraform 変数証明バンドルバケット名
INPUT_S3_BUCKETTerraform 変数入力ファイルのバケット(同上)
INPUT_S3_KEYStep Functions 入力セッション固有の入力パス
OUTPUT_S3_BUCKETTerraform 変数出力先バケット(同上)
OUTPUT_S3_PREFIXStep Functions 入力sessions/{sessionId}/{executionId}/

フェーズ 4: 結果通知

Step Functions が finalize-callback-runner Lambda を呼び出し、以下の情報をセッションに書き戻します。

  • 成功時: bundle metadata(s3BundleKey、presigned URL、有効期限、アップロード時刻)と、bundle.zip / bitmap artifact から復元した finalizationResult
  • 失敗時: 失敗状態とエラー情報(イメージ署名失敗、プローバーエラーなど)

ECS タスクの実行フロー

ECS Fargate タスク内のコンテナは、エントリポイントスクリプトにより以下の処理を順次実行します。

  1. S3 から入力 JSON をダウンロード
  2. 入力の構造を検証(必須フィールド確認)
  3. zkVM ホストバイナリを実行(タイムアウト: 900 秒)
  4. ジャーナルを JSON に変換
  5. public-input.jsonelection-manifest.jsonclose-statement.json を構築
  6. journal.json と公開監査アーティファクトの整合性を検証
  7. bundle.zip を作成(receipt.json + journal.json + public-input.json + election-manifest.json + close-statement.json
  8. 生成物と private sibling artifacts を S3 にアップロード(リトライ付き)

入力の検証

エントリポイントは、zkVM 入力 JSON に必要なフィールドが存在することを確認します。欠落があればタスクは即座に失敗し、Step Functions が失敗コールバックを実行します。

zkVM ホストバイナリの実行

コンテナ内の /opt/zkvm/bin/host がプローバーとして起動されます。タイムアウトは 900 秒(15 分)です。本番モード(RISC0_DEV_MODE 未設定)では実際の STARK 証明が生成され、64 票で約 370 秒を要します。

S3 アップロード

生成されたアーティファクトは、指数バックオフ付きのリトライ(最大 3 回、基底 2 秒)で S3 にアップロードされます。主にアップロードされるファイルは以下です。

ファイル説明
*-receipt.jsonzkVM host の生出力(レシート)
*-output.jsonzkVM host の生出力(集計結果)
*-journal.jsonzkVM host が出力した場合のみ残る生の journal artifact
public-input.jsonエントリポイントが構築する、秘密データを含まない検証用レコード
election-manifest.json選挙設定の公開監査用スナップショット
close-statement.json集計締切時点のログ境界を表す公開監査レコード
included-bitmap.json厳密な counted bitmap。bundle.zip には含めず、隣接オブジェクト(sibling object)として保持
seen-bitmap.json厳密な presented bitmap。bundle.zip には含めず、隣接オブジェクト(sibling object)として保持
bundle.zipreceipt.json + journal.json + public-input.json + election-manifest.json + close-statement.json の配布対象アーカイブ

配布と callback 復元の主経路は bundle.zip 内の receipt.json / journal.json / 公開監査アーティファクトです。bitmap artifact は利用可能な場合に sibling object として callback から追加復元されます。配布経路の詳細は バンドル構造 を参照してください。

SQS キュー設計

ワークキュー

項目設定理由
可視性タイムアウト1000 秒zkVM 実行タイムアウト(900 秒)+ バッファ
メッセージ保持期間4 日一時的な障害からの回復猶予
ロングポーリング20 秒Lambda のポーリングコスト最適化
暗号化SQS マネージド SSEデフォルト暗号化

デッドレターキュー(DLQ)

3 回の受信失敗後、メッセージは DLQ に移動されます。DLQ のメッセージ保持期間は 14 日で、手動での障害調査と再処理に使用されます。

ECS Fargate タスク仕様

項目設定
CPU16 vCPU(16384 ユニット)
メモリ32 GB(32768 MiB)
アーキテクチャARM64(Graviton)
ネットワークモードawsvpc
起動モデルRunTask(サービスなし、1 回限りのタスク)
イメージ指定ダイジェスト固定(@sha256:...
ログドライバーCloudWatch Logs(awslogs)

ARM64 アーキテクチャの選択は、RISC Zero の STARK 証明生成における Graviton プロセッサのコスト効率に基づいています。非 GPU 前提のこの構成は PoC の意図的な制約です。詳細は PoC の意図的な制約 > 非 GPU 前提の証明実行 を参照してください。

クライアント側のポーリング

非同期証明の進捗は、クライアントが GET /api/sessions/:id/status をポーリングして確認します。

stateDiagram-v2
  [*] --> pending: POST /api/finalize → 202
  pending --> running: dispatch-proxy が SFN を起動
  running --> succeeded: callback-runner が結果を書き込み
  running --> failed: エラー発生
  succeeded --> [*]
  failed --> [*]
ステータス説明
pendingファイナライズ要求を受理済み(実装上は pending 更新後に SQS 送信)
runningStep Functions が実行中
succeeded証明生成と結果の書き戻しが完了
failedコールバック経由で失敗が書き戻された状態(署名検証失敗、プローバーエラー等)
timeoutfinalize-callback-runnerTIMED_OUT を受理した場合の状態(現行 State Machine では通常未使用)

注: prover-dispatch-proxy が Step Functions 起動前に失敗した場合、コールバックが走らないため pending のまま再試行/DLQ 待ちになることがあります。

障害時の調査導線

非同期証明がスタックした場合の最小調査パスです。

flowchart TD
  START["finalize がスタック"] --> Q1{"SQS に<br/>メッセージあり?"}
  Q1 -->|No| A1["API → SQS の送信を確認<br/>環境変数 PROVER_WORK_QUEUE_URL"]
  Q1 -->|Yes| Q2{"dispatch-proxy<br/>ログに成功あり?"}
  Q2 -->|No| A2["Lambda ログを確認<br/>SQS → Lambda のトリガー設定"]
  Q2 -->|Yes| Q3{"SFN の<br/>実行状態は?"}
  Q3 -->|署名失敗| A3["ECR イメージ署名を確認<br/>Signer プロファイル設定"]
  Q3 -->|ECS 失敗| Q4{"ECS タスク<br/>ログに出力あり?"}
  Q4 -->|No| A4["タスク起動失敗<br/>IAM / サブネット / イメージ URI"]
  Q4 -->|Yes| A5["エントリポイントエラーを確認<br/>入力検証 / プローバー実行"]
  Q3 -->|コールバック失敗| A6["callback-runner ログを確認<br/>AppSync 書き込み権限"]