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| SIGFAIL["署名検証失敗"]
  ECS --> S3W["S3<br/>bundle.zip 保存"]
  ECS --> CB["finalize-callback-runner<br/>Lambda"]
  SIGFAIL --> 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 を返却(FINALIZE_ASYNC_MODE=false の場合は同期処理のレスポンスを返却)

クライアントはその後、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 を記録

ステージ 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 はタスクの完了を待機し、成功/失敗に応じて対応するコールバックステートに遷移します。

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 を呼び出し、以下の情報をセッションに書き戻します。

  • 成功時: S3 上のバンドルメタデータ(バケット、キー)、証明結果
  • 失敗時: エラー情報(イメージ署名失敗、プローバーエラーなど)

ECS タスクの実行フロー

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

flowchart TD
  A["S3 から入力 JSON を<br/>ダウンロード"] --> B["入力の構造を検証<br/>(必須フィールド確認)"]
  B --> C["zkVM ホストバイナリを実行<br/>(タイムアウト: 900 秒)"]
  C --> D["ジャーナルを JSON に変換"]
  D --> E["public-input.json を構築"]
  E --> F["bundle.zip を作成<br/>(receipt + journal + public-input)"]
  F --> G["S3 にアップロード<br/>(リトライ付き)"]

入力の検証

エントリポイントは、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 の生出力(集計結果)
public-input.jsonエントリポイントが構築する公開入力
bundle.zipreceipt.json + journal.json + public-input.json の公開バンドル

journal.json*-output.json からエントリポイント内で再構成され、bundle.zip の中に receipt.json とともに格納されます。

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 プロセッサのコスト効率に基づいています。

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

非同期証明の進捗は、クライアントが 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 では通常未使用)

障害時の調査導線

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

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 書き込み権限"]