第6巻 処理方式設計書(2) ― ライフサイクル・整合性・セキュリティ・障害系
目次
- 第3章 取引ライフサイクルと状態遷移
- 3.1 状態設計の基本(規範)
- 3.2 txid 状態(規範:最小集合)
- 3.3 タイムアウト制御(規範)
- 3.4 禁止遷移(規範)
- 3.5 gtid/leg 状態(規範)
- 3.6 CASE 状態(規範)
- 第4章 メッセージング方式とI/F設計思想
- 4.0 接続境界(ClientはZCへ直接接続しない)【規範】
- 4.1 非同期I/Fを採る理由(規範)
- 4.2 I/Fのレイヤ
- 4.3 版管理と互換性(規範)
- 4.4 署名と否認防止の立て付け(概要)
- 第5章 冪等性・順序制御・再送制御
- 5.1 前提(規範)
- 5.2 idempotency_key(規範:混線防止)
- 5.3 command_seq / event_seq(規範)
- 5.4 再送制御(規範:枠)
- 5.5 順序制御(規範)
- 5.6 リプレイと再構築(規範)
- 第6章 整合性モデルとファイナリティ設計
- 6.1 整合性モデル(規範)
- 6.2 Decision/Execution分離(規範:中核)
- 6.3 a/b(二段階完了)の定義(規範)
- 6.4 Hモデル(絶対超過禁止:規範)
- 6.5 gtid(多者協調)における整合条件(規範)
- 6.6 高額即時レーン介在の制約と整合(規範)
- 第7章 セキュリティ・認証・署名・否認防止
- 7.1 基本方針(規範)
- 7.2 アイデンティティ(参加主体の識別)
- 7.3 署名(規範:何を署名するかを固定)
- 7.4 Express(PSPR)の署名と短寿命性
- 7.5 秘密情報・個人情報(個情法・業法)
- 7.6 Web3要件への接続(概要)
- 7.7 Watcher(外部レール観測オラクル)の信頼・可用性モデル(規範)
- 第8章 監査・ログ・証憑設計
- 8.1 監査設計の原則(規範)
- 8.2 Finality Log(規範)
- 8.3 Proof参照(bank_proof_ref)(規範)
- 8.4 金額説明可能性(規範)
- 8.5 運用イベントの監査(規範)
- 第9章 障害時・災害時の動作
- 9.1 障害設計の基本(規範)
- 9.2 多拠点障害(地域喪失)
- 9.3 メッセージ基盤障害(Kafka/MQ)
- 9.4 DNS(Daily Netting Settlement)とDNS HOLD(清算未完了による中断:内部要因は残高不足等)
- 9.5 高額即時レーン結果の再照合
- 9.6 RTO/RPO(提出用の枠)
第6巻 処理方式設計書(2) ― ライフサイクル・整合性・セキュリティ・障害系
本巻は
docs/specs/20_method_design.mdの第3章〜第9章(取引ライフサイクルと状態遷移/メッセージング方式/冪等性・順序制御/整合性モデルとファイナリティ設計/セキュリティ・認証・署名/監査・ログ・証憑設計/障害時・災害時の動作)を収める。前巻→第5巻の続き、続きは→第7巻。
第3章 取引ライフサイクルと状態遷移
要旨
本章では、txid/gtid/CASEを中心に、状態遷移(Decision/Execution/a/b)を最小集合として定義し、禁止遷移と収束ルールを明確化する。特に「b=不可逆」の扱いと、SUSPENDED→CASEへの収束で、後からの説明と監査再現性を守る。
3.1 状態設計の基本(規範)
- 状態機械は暗黙にしない。 遷移表+禁止遷移 を規範として固定する。
- Decision(ZC決定)と Execution(参加行実施確認)を同一状態に混在させない。
- 「取消」「失敗」「救済(Reversal)」を相互排他として扱い、顧客表示・監査を破綻させない。
3.2 txid 状態(規範:最小集合)
3.2.1 図解:txid状態遷移(最小・規範)
設計規範(曖昧経路の禁止)
Decision(DECIDED_TO_SETTLE)へ進む経路は、必ず 予約(H_RESERVED もしくは等価の資源拘束)を経由する。
PRECHECKED_SUSPENDED は「外部待ち/夜間停止/承認待ち」を表す前段状態であり、復帰後に H_RESERVED をスキップして Decision へ直行することを禁止する。
stateDiagram-v2 [*] --> RECEIVED: 受付 RECEIVED --> PRECHECKED: 形式/基本検証 RECEIVED --> HTLC_LOCKED: HTLC / HTLC_AUTH canonical 入口 RECEIVED --> DECIDED_CANCEL: precheck 期限超過(T_precheck) PRECHECKED --> PRECHECKED_SUSPENDED: Read-only/外部待ち PRECHECKED_SUSPENDED --> PRECHECKED: 復帰/再開 PRECHECKED_SUSPENDED --> DECIDED_CANCEL: 期限/中止 PRECHECKED --> H_RESERVED: 安全弁予約 PRECHECKED --> DECIDED_TO_SETTLE: HV のみ(H_RESERVED スキップ) H_RESERVED --> DECIDED_TO_SETTLE: Decision確定 DECIDED_TO_SETTLE --> PAYER_EXEC_CONFIRMED: a成立(証憑) DECIDED_TO_SETTLE --> PAYEE_EXEC_CONFIRMED: GTID PAYEE leg のみ(a 省略) PAYER_EXEC_CONFIRMED --> PAYEE_EXEC_CONFIRMED: b成立(証憑) PAYER_EXEC_CONFIRMED --> SUSPENDED: Payee証憑待ち(T3超過) PAYEE_EXEC_CONFIRMED --> SETTLED: 終端 PRECHECKED --> DECIDED_CANCEL: 拒否/期限 H_RESERVED --> DECIDED_CANCEL: 取消Decision DECIDED_TO_SETTLE --> SUSPENDED: 実施遅延/保留 SUSPENDED --> PAYER_EXEC_CONFIRMED: 遅延回復(a) SUSPENDED --> PAYEE_EXEC_CONFIRMED: HV中銀確定(EVT_CB_SETTLED) SUSPENDED --> FAILED_EXECUTION: 期限超過 DECIDED_CANCEL --> CANCELLED: 終端 FAILED_EXECUTION --> [*]
高額即時レーン(HIGH_VALUE)のみ:
PRECHECKED → DECIDED_TO_SETTLE直行を許容する(H 予約スキップ。10_requirements.md§1.2.3 参照)。実装上はsrc/zc/orchestrator/state_machine.ts#ALLOWED_TRANSITIONS.PRECHECKEDに明示列挙し、HV 以外のレーンはtransitionWithLog({ fromState: 'H_RESERVED', ... })でfromStateを縛ることで間接的に禁止する。SUSPENDEDにある取引について、ExternalSettlementAdapter からEVT_CB_SETTLED(中銀決済完了)を受信した場合に限り、SUSPENDED → PAYEE_EXEC_CONFIRMEDを許容する。このガードの正はexternal_settlement_status == 'SETTLED'であり、reason_codeではない(10_requirements.md§1.2.3 の不変条件と同一。実装はsrc/zc/orchestrator.tsの HV 不変条件チェック)。reason_codeを遷移のガードに使わない(規範):reason_codeは窓口向けの説明ラベルであって状態機械の条件ではない(32_api_contracts.md § 状態 reason_code)。かつて本項はreason_code ∈ {SUSPEND_IGS_HOLD, SUSPEND_IGS_PENDING}という条件で書かれていたが、両値は実装に存在せず、ガードは空文だった。判定に使ってよいのは、その目的のために置かれた列(external_settlement_status)だけである。
HTLC / HTLC_AUTH のみ:
RECEIVED → HTLC_LOCKEDを canonical 入口とする。HTLC_LOCKEDで直接 INSERT する経路は禁止し、insertTxWithLog({ initialState: 'RECEIVED', ... })でRECEIVEDを経由してからtransitionWithLogでHTLC_LOCKEDに遷移する(PaymentInitiatedの FinalityLog 痕跡を残すため。背景は10_requirements.md§3.2.3.1-5(HTLC 正準入口の必須性と、その経緯))。GTID PAYEE leg のみ:
DECIDED_TO_SETTLE → PAYEE_EXEC_CONFIRMEDの a 省略遷移を許容する。GTID は GT-level Decision 確定後、レッグ単位のTransactions行をinsertTxWithLog({ initialState: 'DECIDED_TO_SETTLE', ... })で直接生成する(src/zc/lanes/_helpers.ts#ALLOWED_ENTRY_STATESのホワイトリスト経由)。PAYEE 側は対応 PAYER leg のonPayerExecConfirmedから credit を受けるため、自身のTransactions行を持たず Decision 直後に b を観測する。Finality(法的弁済完了)は
PAYEE_EXEC_CONFIRMED(b)にのみ紐付ける 。DECIDED_TO_SETTLEはZCの 実施指示の確定(Raftコミット) であり、資金の不可逆確定とは別軸。RECEIVED/PRECHECKED/PRECHECKED_SUSPENDEDH_RESERVED(安全弁予約)DECIDED_TO_SETTLE/DECIDED_CANCEL(Decision)PAYER_EXEC_CONFIRMED(a成立:証憑)PAYEE_EXEC_CONFIRMED(b成立:証憑)SETTLED(照会上の終端)SUSPENDED(Decisionはあるが実施が遅延・保留)FAILED_EXECUTION(Decisionはあるが実施未確定で終端)CANCELLED(取消終端)
注:
REVERSALは状態ではなく、別txidとして起案される「救済取引」である。
3.2.2 図解:HTLC状態遷移(規範)
stateDiagram-v2 [*] --> RECEIVED: 受付 RECEIVED --> HTLC_LOCKED: H予約 + lock成立 HTLC_LOCKED --> HTLC_FULFILL_REQUESTED: secret提示 HTLC_LOCKED --> HTLC_ONCHAIN_PENDING: クロスチェーンlock観測(CrossChainLocked、`30_internal_design.md` §15.6) HTLC_ONCHAIN_PENDING --> HTLC_FULFILL_REQUESTED: オンチェーンrelease観測(OnchainProofObserved) HTLC_FULFILL_REQUESTED --> DECIDED_TO_SETTLE: secret検証OK DECIDED_TO_SETTLE --> PAYER_EXEC_CONFIRMED: a成立 PAYER_EXEC_CONFIRMED --> PAYEE_EXEC_CONFIRMED: b成立 PAYER_EXEC_CONFIRMED --> SUSPENDED: Payee証憑待ち(T3超過) PAYEE_EXEC_CONFIRMED --> SETTLED: 終端 HTLC_LOCKED --> DECIDED_CANCEL: timelock超過 HTLC_ONCHAIN_PENDING --> DECIDED_CANCEL: timelock超過/オンチェーン不成立 HTLC_LOCKED --> HTLC_LOCKED: secret不整合(Claim拒否・状態維持・再試行可) HTLC_FULFILL_REQUESTED --> FAILED_EXECUTION: 実施未確定で終端 DECIDED_CANCEL --> CANCELLED: 終端 DECIDED_TO_SETTLE --> SUSPENDED: 実施遅延/保留 SUSPENDED --> PAYER_EXEC_CONFIRMED: 遅延回復(a) SUSPENDED --> FAILED_EXECUTION: 期限超過 FAILED_EXECUTION --> [*]
規範(要点)
- HTLCは「取消の代替」ではなく、 成立前の不確実性(条件待ち)を状態化 する。
secretはZCに永続保存しない。保持するのはsecret_hashと 検証証跡 のみ。- timelock到来は必ず
DECIDED_CANCELへ収束し、宙ぶらりんを残さない。 - 取消可能なのは
HTLC_LOCKED/HTLC_ONCHAIN_PENDINGまで(timelock 到来でDECIDED_CANCEL)。preimage 不整合の Claim はHtlcClaimRejectedを証跡化するだけで状態はHTLC_LOCKEDのまま(期限まで再試行可)。secret 検証 OK でHTLC_FULFILL_REQUESTEDに進んだ後は取消不可で、DECIDED_TO_SETTLE(成立)かFAILED_EXECUTION(実施未確定で終端)のいずれかへ収束する(ALLOWED_TRANSITIONS.HTLC_FULFILL_REQUESTED)。
3.2.3 gtid/leg 状態遷移(規範)
参照:GTID 集約状態(
GT_*)の正準遷移表・SoT は §3.5。本節はその補足として、レッグ準備モデル(Try/Confirm/障害時の扱い)を述べる。
GTIDは多者間の整合性を重視するため、以下の「不整合排除モデル」を採用する。
- Decision前の確保(Try) :
GT_PRECHECKED段階で、全Payerの「Hard Reservation(別段確保)」と、全Payeeの「受入体制」を確認する。 - Decision後の確定(Confirm) :Decision確定後は、Payer/Payee共に「銀行別段間」の資金移動となるため、口座状態(残高不足・凍結)に起因するExecution失敗は発生しない。
- システム障害時の扱い :
- 通信障害・勘定系停止等によりExecutionが確認できない場合は、従来通り
GT_SUSPENDEDとし、CASE(運用例外)へ接続する。 - 規範 :この場合の解決策は「システム復旧後の再適用(Forward Recovery)」または「強制b成立(Custody化)」とし、口座都合を理由としたReversal(組戻し)は行わない。
- 通信障害・勘定系停止等によりExecutionが確認できない場合は、従来通り
3.3 タイムアウト制御(規範)
3.3.1 タイムアウトと状態遷移(規範)
タイムアウトは“暗黙”にせず、 どの状態を、どの理由で止めるのか を固定する。
下表の reason_code は状態 reason_code(32_api_contracts.md § 状態 reason_code)である。
規範として先に固定したが未実装のものは 【未実装】 を付す(30_internal_design.md §10.0)。
| タイマ | 適用状態 | 期限超過時の遷移 | 状態 reason_code |
|---|---|---|---|
| T_precheck | RECEIVED | DECIDED_CANCEL | CANCEL_PRECHECK_TIMEOUT |
| T_namecheck | PRECHECKED | PRECHECKED_SUSPENDED | SUSPEND_NAMECHECK_PENDING |
| T_auth | PRECHECKED | PRECHECKED_SUSPENDED | SUSPEND_AUTHORITY_PENDING |
| T_batch_window | PRECHECKED | PRECHECKED_SUSPENDED | COUNTERPARTY_WINDOW_CLOSED |
| T2_exec | DECIDED_TO_SETTLE | SUSPENDED | SUSPEND_EXEC_TIMEOUT |
| T3_payee_proof | PAYER_EXEC_CONFIRMED | SUSPENDED | SUSPEND_PAYEE_PROOF_TIMEOUT |
| T_gt_deadline | GT_PRECHECKED | GT_DECIDED_CANCEL | PRECHECKED_STUCK_TIMEOUT(FinalityLog payload の reason。GTID 側に reason_code 列は無い) |
※ パラメータ値は第10章(運用設計)で定義し、変更時は所定の変更管理(合意・証跡)を要する。
T_authの規範(AML/制裁照会は fail-closed):Authority Check の答えは OK / NG の 2 つではなく、「答えが返らない」が third case として存在する(払い手銀行の Circuit が OPEN、
ingress が判定を返さない等)。判定不能を「NG でなければ通す」と扱ってはならない——
到達できなかった照会が制裁非該当と同じ効果を持つことになり、審査そのものが無効化される。
したがって判定不能は 取引を進めず
PRECHECKEDに留置し、T_auth の起点とする。一方で 判定不能は NG ではないので、その場で取消してもならない(一過性の障害で正当な
送金を落とすことになる)。期限超過で
PRECHECKED_SUSPENDED(SUSPEND_AUTHORITY_PENDING)へ落とし、人が扱える状態にする。留置の印は
Transactions.reason_codeに同じ
SUSPEND_AUTHORITY_PENDINGを先置きすることで表し、要求時刻は専用列Transactions.pending_sinceが担う。留置の印を持つ行だけを掃引するので、別の理由で
PRECHECKEDに滞留した行が AML 待ちと誤って表示されることはない。実装:
src/zc/lanes/_authority_check.ts(留置)とsrc/cron/timeout_sweep.ts(掃引)。なぜ専用列か(規範):本項はかつて「要求時刻は
updated_atが担う(専用列は設けない。他のタイマも同じ計り方である)」と定めていた。これは誤りで、しかも
T_auth だけでなく下表の 4 タイマすべてが誤っていた。
updated_atはこの行へのあらゆる書込みで動き、進捗と無関係な書込みにも状態ガードが無い——CASE 起票時の
case_id(src/zc/cases/case.ts)、送金内容データ連携時のedi_ref(
src/zc/richdata/edi.ts)。したがって滞留した取引に CASE を起票するという、
まさにその滞留に対する運用者の動作が、当該取引自身の期限を後ろへずらしていた。繰り返せば無限にずれる。タイマを最も必要とする行ほど、待っている間に別の理由で
触られる機会が多いからである。
「留置の印を持つ行だけを掃引する」という上の一文はどの行を掃くかの保証であって、
経過時間をどう測るかの保証ではない。両者を取り違えたことがこの誤りの原因である。
是正として
Transactions.pending_sinceを新設した。書込み元はレーン共通プリミティブ(
insertTxWithLog/transitionWithLog)と本節の留置印に限られ、付随的な書込みからは構造上到達できない。掃引側は
COALESCE(pending_since, updated_at)を読む(COALESCE は本列の導入前に書かれた行のための後方互換)。回帰試験は
test/cron/pending_since.test.ts、形の固定は
test/invariants/pending_since.test.tsにある。なお
GtidTransactionsの 2 タイマ(§3.5)はupdated_atを読み続けてよい。当該表への
UPDATEはコード上すべてstateの遷移を伴うため、updated_atが状態への進入時刻と一致するからである。これは現在の呼出し側の性質であって
スキーマの性質ではないので、非遷移の書込みが追加されたら検出されるよう
test/invariants/pending_since.test.tsで静的に固定している。同じ「判定不能を通さない」規範は HTLC claim 直前の再照会にも及ぶ。ただしそちらは待機
状態を持てない(待つことが timelock と競合する)ため、帰結は留置ではなく claim の拒否
になる。
30_internal_design.md§15.4 を正とする。
T_precheckの掃引はsrc/cron/timeout_sweep.tsの先頭ステップにある。T2_execと同じくBULK / DEFERRED は対象外(窓を待つ状態を滞留と見なさないため)で、
owner='ZC'の行だけを触る(第4章の単一所有者則)。
RECEIVEDには Decision がまだ無いので補償すべきものが無く、掃引は中断ではなく
DecidedCancelを経て終端CANCELLEDへ落とす。
3.4 禁止遷移(規範)
- b成立後にCANCELLEDへ遷移してはならない 。
- 証憑なしにPAYER_EXEC_CONFIRMED / PAYEE_EXEC_CONFIRMEDへ遷移してはならない 。
- FAILEDを黙って取消扱いにしてはならない (顧客表示・監査が破綻)。
3.5 gtid/leg 状態(規範)
- gtidシャードで GT_DECIDED_TO_SETTLE / GT_DECIDED_CANCEL を確定(SoT)。
- legは結果単位(特にPAYEE側の証憑)として保持。
- 期限切れ(leg expires_at)等により一部が前進しない場合は、GTを
GT_SUSPENDEDとし、CASEに接続したうえで Forward Recovery(復旧後の再適用/再照合)+未実行証明による解放(必要に応じCustody化) により収束させる。口座都合を理由としたReversalは行わない。物理的に資金移動が不能であると証明された場合に限り、別取引としてReversalを許容する。
実装(状態機械の単一の正) :GTID 集約の合法遷移は
src/zc/orchestrator/gtid_state_machine.tsのALLOWED_GTID_TRANSITIONSに宣言として固定する(
TxStateのstate_machine.ts#ALLOWED_TRANSITIONSと対称)。終端化経路は
assertValidGtidTransitionで実行時検証し、生 SQL の全 GTID 遷移は静的検査
(
test/zc/gtid_state_machine.test.ts)で宣言グラフに含まれることを保証する。規範遷移:
GT_RECEIVED → GT_PRECHECKED → GT_DECIDED_TO_SETTLE → GT_SETTLED、
GT_PRECHECKED → GT_DECIDED_CANCEL → GT_CANCELLED、
GT_DECIDED_TO_SETTLE → GT_SUSPENDED(terminal leg 失敗)、
GT_SUSPENDED → {GT_DECIDED_TO_SETTLE(resume), GT_FAILED}。
3.6 CASE 状態(規範)
- CASEは「例外処理のチケット」であり、取引状態と因果リンクを必ず持つ。
- CASEは監査・当局・窓口照会の共通キー(case_id)となる。
第4章 メッセージング方式とI/F設計思想
要旨
本章では、接続境界(Clientは直接ZCに接続しない)と、非同期I/Fを採る理由を示す。I/F契約を固定するためのレイヤ分離、版管理・互換性・署名設計の原則を整理し、
30_internal_design.md第12章(I/F契約)・第13章(メッセージ定義)の読み方へつなげる。
4.0 接続境界(ClientはZCへ直接接続しない)【規範】
結論(設計上の原則) :一般のClient(個人アプリ・POS・加盟店端末等)は、ZCへ直接メッセージを送信してはならない。
ZCが受け付けるのは、 参加主体(Bank/認可事業者)のみ とし、Clientは必ず参加主体の Edge/API を経由する。
- セキュリティ :ZCを公開面に晒さず、DDoS/不正接続/鍵漏えいの影響面を縮小する。
- 責任分界 :顧客認証・限度・与信・不正検知は参加主体責任。ZCは“決定と証跡”に専念する。
- 互換性 :参加主体がチャネル差分(モバイル/店頭/法人)を吸収し、I/F契約を壊さず進化できる。
- 監査容易性 :誰が送ったか(署名主体)が明確になり、争点を減らす。
例外:参加主体として認定された事業者(Licensed Participant)は、Bankと同等にZCへ接続できる。
以降の図表では、Client発の操作(受付/照会/HTLC secret提示)は PayerBank(Edge/API) を経由する前提で記述する。
4.1 非同期I/Fを採る理由(規範)
- ピーク吸収 :給与・自治体・EC等のピークを平準化し、参加行側の接続コストを下げる。
- 運用分離 :参加行障害が全体停止に直結しないようにする。
- 低廉 :共通基盤の再利用で単価を下げる。
- 代償として、重複・遅延・欠番は「必ず起きる」。よって I/F契約の固定 が前提。
規範
I/F契約は“骨子”ではなく“固定”である。例示や口頭合意では、合同レビュー会・監督・RFPで必ず破綻する。
4.2 I/Fのレイヤ
- 同期API :受付(受理/拒否)、照会(Read Model)。
- 非同期cmd/event :実施要求、実施確認、決定通知、例外・運用イベント。
- スキーマ管理 :Schema Registry(互換性ポリシー)+署名正規化(canonicalization)をセットで運用標準化。
4.3 版管理と互換性(規範)
- スキーマは
schema_versionを必須とし、破壊的変更は メジャー更新 。 - 互換性は原則「後方互換(backward)」を採用し、移行期間は旧新版の併走を許容。
- 署名対象(signed_fields)と正規化(canonicalization)は、破壊的変更を避けるため 強く固定 する(
30_internal_design.md第12章(I/F契約))。
4.4 署名と否認防止の立て付け(概要)
- 署名は「アルゴリズム」より先に「何を署名するか」を固定する。
- 署名対象外のフィールドを勝手に増やすと、監査・訴訟で争点化するため禁止(
30_internal_design.md第12章(I/F契約))。
第5章 冪等性・順序制御・再送制御
要旨
本章では、at-least-once配送を前提に、重複・遅延・欠番を事故にしないための冪等性、seq、再送制御、DLQ→CASEの運用接続を定める。ログからのリプレイ/再構築まで含め、実装がぶれやすい固定点を押さえる。
5.1 前提(規範)
- 配送は at-least-once。重複・遅延・欠番は必ず起きる。
- 全体順序は要求しない。 同一aggregate内の順序のみ を契約で固定する。
5.2 idempotency_key(規範:混線防止)
- 論理コマンド単位で一意・永続。再送時は同一キー必須。
- スコープを強制的に分離し、txid/gtid/leg/attempt混線を防ぐ(
30_internal_design.md§12.4)。
例(固定フォーマット推奨)
TX:{txid}:{name}:{issuer}GT:{gtid}:{name}:{issuer}LEG:{gtid}:{leg_id}:{name}:{issuer}RTP:{rtp_id}:{attempt_id}:{name}:{issuer}
5.2.1 二重送信(重複)の収束規範
- COMMANDの重複:同一
idempotency_keyは 同一要求 として扱い、副作用は1回のみ。 - EVENTの重複:同一
bank_proof_ref(または同一proof_digest)は重複として無害化し、取り込み1回。 - 内容不一致:同一txidで不整合(amount/宛先/署名)がある場合は
PROOF_MISMATCHとしてCASEへ接続し、人手判断へ昇格。
規範 :二重送信は異常ではない。二重計上が異常である。
5.3 command_seq / event_seq(規範)
- 永続・単調増加・巻戻り不可 。欠番は許容するが再利用不可。
- 粒度: 送信者×aggregate (
30_internal_design.md§12.5)。
5.4 再送制御(規範:枠)
- 再送は指数バックオフ+ジッタ。上限回数(
PR-RETRY-MAX。30_internal_design.md§12.9)を設ける。 - 上限到達はDLQへ落とし、 必ずCASEへ接続 (運用の入口)。
- 再送時の副作用は禁止(冪等処理)。
5.5 順序制御(規範)
- Kafka等のキー順序に依存する場合でも、 アプリ層でseq検証 を実施する。
- seqギャップ(欠番)は許容だが、巻戻りは拒否(監査破綻)。
- 直列化キーの推奨:
txid、gtid、RTP:{rtp_id}:{attempt_id}、CASE:{case_id}。
5.6 リプレイと再構築(規範)
- Finality Logを起点にRead Modelを再構築可能であること。
- 「二重適用」を避けるため、再構築処理もidempotentであること(同一ログの再適用で同一結果)。
第6章 整合性モデルとファイナリティ設計
要旨
本章では、SoT(Finality Log)とRead Modelの整合性モデルを示し、DecisionとExecutionの分離、a/b(二段階完了)と資金帰属の扱いを定義する。安全弁Hの更新規則、gtidの整合条件、高額即時レーン介在時の制約を整理し、設計上の「詰ませない」条件を固定する。
6.1 整合性モデル(規範)
- SoTはFinality Log 。Read Modelは派生であり、整合性は「最終的整合」を許容する。
- ただし、決済は争点化し得るため、最終的整合を採りつつ 説明可能性 を最優先する。
精密化(原則1の射程)
FinalityLog が「唯一の正(SoT)」であるのは 協調事実——ZC の決定(Decision)・所有権移転(単一所有者則)・受領した署名付き証明——についてである。金銭事実(実際に資金が動いたか)の正は各参加行の元帳にある(ZC は元帳を持たず・触らない)。したがって ZC の正確な主張は「乖離しない(原子性)」ではなく「すべての乖離は有界時間内に検出され、帰責される(監査可能性)」である。これは原則1の弱化ではなく精密化であり、§6.3.3 の三層構造・単一所有者則(
30_internal_design.md)と噛み合って法的な物語が一貫する。
6.2 Decision/Execution分離(規範:中核)
- Decision :ZCが「実施指示を出すこと」を確定(Raftコミット)。
- Execution :参加行が「資金状態が不可逆に確定した」ことを証憑で示す。
- これを混同すると、障害時に「決めたのに実行されていない」または「実行されたのに決めていない」の説明が破綻する。
6.3 a/b(二段階完了)の定義(規範)
6.3.1 b(弁済完了)の再定義とCustody(別段預り)
本基盤では、口座状態(凍結・解約等)による不整合を排除するため、bの定義を以下の通り厳格化する。
- a(支払人完了) :支払人資金が不可逆に銀行の決済処理領域(支払用別段預金等)へ移ったこと(
PAYER_EXEC_CONFIRMED)。 - b(弁済完了) :受取銀行の管理下(受取用別段預金等)に資金が着金し、受取銀行がこれを受領確認したこと(
PAYEE_EXEC_CONFIRMED)。- 規範 :顧客口座への入金(Credit)は、b成立後の受取銀行内部責務とする。
- Custody(別段預り) :顧客口座解約・凍結等により入金不能な場合は、受取銀行が 「預り金(Custody)」 として保全し、状態として管理する。これを理由に決済自体を失敗(Reversal)させてはならない。
6.3.2 失敗と救済(Reversal)の適用範囲
b成立後の取消は禁止とし、b未成立時の扱いは以下の通り固定する。
- 口座起因の不整合(禁止) :残高不足・口座なし等を理由とする b拒否は認めない(Hard Reservation / Hard Landing / Custody で吸収)。
- システム災害・不整合(CASE) :通信途絶・勘定系全損等により b証憑 が発行不能な場合に限り、CASE(例外)へ収束させる。
- 回復方針 :原則として「Forward Recovery(復旧後の再適用・強制Custody化)」を優先する。
- Reversal :物理的に資金移動が不能であると証明された場合(
CreditFailedProof)にのみ、別取引として Reversal を許容する。
6.3.3 ファイナリティの三層分離(債務確定・決済資産確定・証跡確定)【概念】
完了性(finality)は一枚岩ではなく、三つの層に分けて語る必要がある。§6.3.1 の b の定義はこのうち第1層(債務確定)を指しており、残る二層と混同してはならない。
- (1) 債務確定(obligation finality) :参加者間で「この取引は成立したものとして扱う」ことが規程上不可逆になる点。b(
PAYEE_EXEC_CONFIRMED)はここに位置する。確定点を定義するのは台帳ではなく規程であり、法がそれを保護する(全銀システムや CLS の PvP も同型)。規程上の正式名としてObligationFinalを与える。 - (2) 決済資産確定(settlement finality) :中央銀行マネー(日銀ネット)ないし各行元帳での実際の付替え。ZC はこの資産を保有も制御もしない。実装上の対応事実は
Transactions.external_settlement_status(IGS/BOJ 決済確認)・DNS サイクル決済事実・クロスチェーンの確認深度到達であり、これらを b とは別の記録事実(settlement_finality_ref相当)として保持する。 - (3) 証跡確定(evidential finality) :FinalityLog のハッシュ連鎖。「何が起きたかの改ざん不能な記録」であり、上記 (1)(2) の事実列そのものを固定する。
含意 :b は決済を*主張(assert)するのをやめ、決済を参照(reference)*する。この分離により「operational state を legal finality と混同している」という批判は解消する(主張を取り下げ、参照に変えたため)。
ゼロアワー問題への注記(法的限界)
参加行の破綻手続開始は、当日朝に遡って既了取引を無効化し得る(ゼロアワー・ルール)。ハッシュチェーンは改ざんを検出できても破産管財人には対抗できない。b の「不可逆」を法的に実在させるには、完了性保護(指定システム化等)の取得が前提であり、これは技術では代替できない制度事項である(制度面は
10_requirements.md第4章 を参照)。
6.4 Hモデル(絶対超過禁止:規範)
Hは「超過しない」ことが目的の安全弁であり、過検知を許容する。Hはtxid単位で管理し、H_reserved と H_locked の2層で表す。
H_reserved:Decision前の予約枠(取消可能)H_locked:Decision後に凍結される枠(DNS清算完了まで解放しない)
6.4.0 更新規則(規範:台帳更新を固定)
| トリガ(Finality Log) | 事前条件 | 予約/凍結更新 | 備考(監査説明) |
|---|---|---|---|
| HReservationPlaced | - | H_reserved += amount |
予約開始 |
| DECIDED_TO_SETTLE | H_reserved存在 | H_reserved -= amount / H_locked += amount |
予約→凍結へ移送(Decision確定) |
| DECIDED_CANCEL | Decision前 | H_reserved -= amount |
予約を取消(凍結は作らない) |
| DNS_CYCLE_SETTLED | - | H_locked -= sum(amount where settled_in_cycle) |
DNS清算完了で枠解放(参加行単位) |
| NoDebitRecordedProofSubmitted | a未成立 | H_locked -= amount |
未実行証明に基づく解放(Decision後の救済) |
| HUnlockAuthorized | CASE承認 | H_locked -= amount |
運用二重統制による解放 |
| ReversalSettled | reversal_of_txid を伴う救済取引の成立 |
H_locked -= 0(変化させない) |
意図した零。救済取引は §6.4.2 により H チェックの対象外だが、それは判定の話であって台帳更新の話ではない。本行が無いと「表に無い=変化しない」という既定に落ちるだけで、保守側へ倒した判断なのか書き漏らしなのかを後から区別できない。減少させない選択の理由は、元取引の H_locked は元取引自身の解放契機(DNS 清算完了・未実行証明・4 眼)で外すものであり、救済取引の成立をもって二重に外すと上限が破れるためである |
規範
Decision後は「取消」ではなく「救済(補償)」で収束する。よって
DECIDED_CANCELをH_locked解放トリガとしてはならない。
関連(H を動かさない例外):
MisrecordCorrected(誤記録訂正、10_requirements.md§4.4「唯一の超例外」)は、上表のどの行にも該当しない——「a が誤記録された」場合のみを対象に、時間窓・4 眼承認・evidence を必須とし、取消ではなく記録訂正として FinalityLog に追記して CASE へ収束させるものであり、H 台帳を巻き戻さない。b 成立後は不可逆につき Reversal へ誘導する(契約は32_api_contracts.md § POST /api/transfers/:txid/misrecord-correct)。
6.4.1 H_locked解放(FAILED_EXECUTION時の詰み防止)【規範】
DECIDED_TO_SETTLE 到達後は当該枠を H_locked に移送し凍結するが、 FAILED_EXECUTION(Decisionはあるがa未成立) が長期化すると運用事故になり得る。
本書は、以下の 解放の証跡条件 を規範として固定する。
自動解放してよい条件(機械判定)
- 参加主体(PayerBank)が 未実行証明(NoDebitRecordedProofSubmitted) を発行し、ZCが署名検証のうえ Finality Log に記録した場合
自動解放してはならない条件(補償が必要)
- a成立(PAYER_EXEC_CONFIRMED)済みの場合(資金は不可逆に移動している)
- b成立(PAYEE_EXEC_CONFIRMED)済みの場合(弁済完了である)
運用解放(CASEで二重統制)
- 上記の証明が得られない場合は、CASEにおいて 二重統制(4眼) で
HUnlockAuthorizedを発行する。 - その際、根拠として 台帳照合ハッシュ/照会応答署名/Authority Check(AML/制裁)結果 のいずれかを添付し、監査で追跡可能にする。
- 上記の証明が得られない場合は、CASEにおいて 二重統制(4眼) で
目的:堅牢性(自動解放しない)と運用可能性(詰ませない)を両立する。
6.4.2 救済取引(Reversal)のH特例【規範】
救済取引(Reversal)は「トラブル解消のための緊急避難」であり、H(Net Debit Cap)により起票不能となってはならない(救済のデッドロック防止)。
- 適用:
reversal_of_txidを伴い、かつ制度で定義された救済理由(誤入金・重複・物理的不可能等)に該当する取引のみ。 - 取扱い:Reversalは Hチェック(
H_reserved/H_locked)の対象外 とし、H超過を理由にREJECTしてはならない。 - 統制:H超過状態でのReversal起票はCASE経路(運用二重統制)に接続し、監査証跡(承認記録)を必須とする。
6.5 gtid(多者協調)における整合条件(規範)
- 一体性の対象は「Decision(実施指示の確定)」である 。Execution(a/b)はネットワーク・参加行都合により部分進捗が起き得るため、Decision後の不整合は CASE+補償で必ず収束 させる。
- GT_DECIDED_* はgtidシャードFinality Logで確定(SoT)。
- 各payerシャードへ決定証明(decision_proof_ref)を参照追記し、監査で辿れること。
- legの一部失敗・期限切れは GT_SUSPENDED + CASE とし、補償で全体を安全側に収束(Decision後の取消は禁止)。
6.5.1 確定等級(Finality Grades)による多脚原子性の精密化【概念・未実装】
異種基底の多脚取引(GTID × HTLC × FX PvP など)では、脚ごとに確定の強度が異なるため、基底を勘定に入れると素朴な「all-or-nothing」は成立しない。そこで脚ごとに確定等級を導入して精密化する(概念であり、現時点で未実装)。等級は §6.3.3 の三層分離および 30_internal_design.md §15.6(onchain_finality_class:PROBABILISTIC / DETERMINISTIC)と対応する。
| 等級 | 意味 |
|---|---|
F0 |
取消可能(勘定系の日中仮記帳) |
F1 |
規程確定(b。倒産隔離は指定の有無に依存=§6.3.3 の債務確定) |
F2 |
決済資産確定・確率的(パブリックチェーン。reorg 確率 ε 付き) |
F3 |
決済資産確定・決定的(日銀ネット・私設チェーン) |
- 上限則 :GTID が主張できる原子性は
min(全脚の等級)を超えられない。 - 運用則 :最も不可逆な脚を最後にコミットする/無条件の不可逆脚は GTID あたり最大 1 本(2 本目以降はハッシュロックで条件化するか、PvP エージェント経由に強制する)。さもなくば二通貨 RTGS × RTGS が Herstatt リスクを再導入する。
- 正確なラベル :GTID の保証は「all-or-nothing」ではなく「有界 unwind 集合付きの無損失協調」と表現するのが正しい(多脚原子性の詳細は本書 第17章(クロスカレンシーFX 処理方式)を参照)。
6.6 高額即時レーン介在の制約と整合(規範)
- 規範 :高額即時レーン介在時は多者協調の完了認定(全legのb一致)を保証しない。
- 規範 :高額即時レーンではgtidを用いない(txid単位)。
- ExternalSettlement Adapterは
ext_instruction_idを冪等キーとし、結果再照合でRead Modelを再構築する。
対外説明テンプレ(骨子)
「当基盤は、決定(Decision)と実施確認(Execution)を分離し、どの時点で何が確定したのかを監査証跡で説明できます。安全弁(H)により、障害時も上限超過による連鎖破綻を防ぎます。」
6.6.1 高額即時レーンの取消要求(顧客要望)と行き違い防止(規範)
- 規範 :高額即時レーンにおける顧客の「やめたい」は、元取引の取消ではなく 返金(b)で収束 させる。
- 参加主体側の受付 :PayerBankは、顧客取消を受理した時点で
INTERNAL_CANCEL_ACCEPTEDをZCへ通知できる。
6.6.1.1 行き違い防止ゲート(Must)
ZCが清算サービスへ IGS振替依頼を送信済み であり、まだ結果(IGS_RESULT/BOJ_SETTLED 等)を受理していない間、
ZCはPayerBankへ内部取消の承認(CONFIRM/ACCEPT)を返してはならない 。
- IGS結果が未確定の間(
external_settlement_status == 'REQUESTED')はnext_action_hint=WAITとして保留する。「まだ返ってきていない」の判定はexternal_settlement_statusが担う——reason_codeは説明ラベルであって判定には使わない(32_api_contracts.md § 状態 reason_code)。next_action_hintの値域は30_internal_design.md§13.6 が正。 - IGS結果が「未成立(HOLD継続/不成立)」で確定した場合のみ、内部取消を確定し、返金(返し決済)へ遷移させる。
- IGS結果が「成立(SETTLED)」で確定した場合、元取引の取消は不可能であり、救済は返金取引(別txid)で行う。
第7章 セキュリティ・認証・署名・否認防止
要旨
本章では、メッセージ単位の検証(mTLS+署名)を前提に、参加者アイデンティティ、鍵管理、署名対象の固定、否認防止を整理する。Express(PSPR)の短寿命性、個人情報最小化、Web3/外部台帳等への接続を「責任分界が崩れない範囲」に留める。
7.1 基本方針(規範)
- ゼロトラスト前提 :ネットワーク境界ではなく、メッセージ単位で検証する。
- mTLS+メッセージ署名 :通信路と内容の両面で改ざん・なりすましを防ぐ。
- 否認防止 :誰が何を送ったかを、署名とFinality Logで立証する。
7.2 アイデンティティ(参加主体の識別)
- 参加主体(org_id/system_id)を発行主体として固定し、証明書・鍵をTrust Registryで管理。
- 鍵ローテーション・失効は運用イベントとしてログ化(監査)。
7.3 署名(規範:何を署名するかを固定)
- 署名アルゴリズム(alg)は要件で確定可。
- ただし、 署名対象(signed_fields)と正規化(canonicalization)は固定 (
30_internal_design.md§12.6)。 - 金額・識別子・時刻・相関ID・証憑参照は最低限署名対象とする。
7.4 Express(PSPR)の署名と短寿命性
設計規範(PSPR参照の可用性責務)
PSPR本文のSoTはPayeeBankである。ただし Express の同期応答品質を担保するため、pspr_ref参照は高可用であることを参加条件とする。
さらに監査・障害時の参照断を防ぐため、PayeeBankはPSPR登録時に 要約(必須キー+digest+発行署名)をZCへ送付し、ZCはこれを WORM保全(規範)する(本文そのものは原則保持しない)。
参照障害時は、(1) Vault短期複製(許容)または (2) 同期応答を
INGRESS_ACCEPTEDに落として非同期処理へ切替(規範)し、不完全な宛先情報でDecisionを確定しない。
- PSPRは加盟店/GW(受取側)が生成し、 PayeeBankが発行主体として登録・払い出し(pspr_ref)する。
- PSPR本文の 保管責任(SoT)はPayeeBank に置く(短寿命・最小化)。ZCは
pspr_refと digest(参照証跡) を保持し、本文は原則保持しない。 - PSPRは短寿命(例:60〜180秒)。nonceの再利用は禁止し、ZCはdigest単位で重複検知。
- 受取銀行CapabilityStateがAcceptingでない場合は同期REJECT(事故防止)。
7.5 秘密情報・個人情報(個情法・業法)
- Vaultで短期保持し、最小化(必要最小限・目的限定)。
- 監査・照会に必要な範囲は参照証跡(digest/参照番号)として保持し、本体は削除可能にする(第8章)。
7.6 Web3要件への接続(概要)
- HTLC(hashlock+timelock)を 外部参照なし で提供し、Web3連携(秘密値提示等)に耐える。
- 業務条件オラクル(「納品された」等、取引の成否そのものを外部主体に判定させる仕組み)は、制度・責任分界が不明確になるため本基盤の規範対象外とする(
10_requirements.md第4章 法制度・契約構造との整合性で整理)。 - 他方、クロスチェーン決済は外部レールの確定イベントの観測を要するため、ZC は限定された権限のみを持つ Watcher(外部レール観測オラクル) を導入する。Watcher は取引の成否を判定せず、外部レールで起きた事実(エスクローのロック/解放)を署名付きで観測するだけであり、その信頼境界・可用性・残存リスクは §7.7 で規範化する。両者(業務条件オラクル=対象外/Watcher=限定導入)を明確に区別する。
7.7 Watcher(外部レール観測オラクル)の信頼・可用性モデル(規範)
要旨
クロスチェーン決済は外部レールの確定観測を要し、ZC コアはチェーンを直接検査しない(
recordCrossChainLock/recordOnchainFulfillment)。本節は、その観測を担う Watcher の権限境界・信頼水準(k-of-n 定足数と equivocation)・可用性(ライブネス)設計・残存リスクを規範化する。§7.6 で規範対象外とした「業務条件オラクル」とは別物として明確に切り分ける。
7.7.1 Watcher の定義と権限境界(規範)
- Watcher は、外部レール(オンチェーン/IGS/海外中銀レール)で既に起きた確定事実(エスクローのロック/プリイメージ公開による解放)を観測し、署名付き
SettlementProofRefとして ZC に提出するだけの主体である。 - Watcher は取引の成否を判定しない。式の真偽・条件成立の裁定は行わず、観測は append-only の事実申告にとどまる(だから §7.6 の「業務条件オラクル」の責任分界問題を負わない)。
- ZC は Watcher 観測をそのままでは終端化しない:(a)
preimageがhashlockに一致すること、(b) 確認深度ゲート通過、(c) 外側 timelock 未満了——を満たさない限りDECIDED_TO_SETTLE(b)に進めない。Watcher の権限は「決済を進める提案」に限られ、「決済を確定させる権限」は持たない。
7.7.2 信頼水準:k-of-n 定足数と equivocation 検知(規範)
観測の信頼水準は、「誰が署名したか」(認可)と「何人の独立した運用主体が一致したか」(定足数)の二段で決まる。両方を規範として固定する。
認可(署名検証) :Watcher は KeyRegistry に
owner_type='EXTERNAL_RAIL'|'ATTESTER'で登録された鍵で署名し、ZC は署名・nonce・時刻スキューを検証する(verifyExternalSignature、WATCHER_UNAUTHORIZED)。(source, external_ref)を冪等キーとし、同一 Watcher による同一イベントの再報告は重複排除される(最初に検証成功した観測が記録され、以降はdeduped)。定足数(k-of-n)【規範】 :ひとつの外部イベントの終端化には、相異なる Watcher 運用主体(
KeyRegistry.owner_ref。鍵ではなく operator で数える) による一致観測がHtlcContracts.onchain_min_watchers個そろうことを要する。閾値未達の間、当該 HTLC はHTLC_ONCHAIN_PENDINGに留まり、応答はreason_code=ONCHAIN_QUORUM_PENDING、証跡はOnchainQuorumPending(have_watchers/required_watchersを含む)として FinalityLog に残る。単一の Watcher 鍵だけでクロスチェーン脚を決済させてはならない。確認深度は「最も浅い独立観測」を採る【規範】 :確認深度ゲート(
30_internal_design.md§15.6 のrequiredConfirmations)の判定には、定足数を構成する相異なる運用主体の申告のうち最小値を用いる。単独の Watcher が自己申告の深度を水増ししてゲートを解除することを構造的に封じるためである。equivocation は fail-closed【規範】 :同一
(source, external_ref)に対し矛盾する主張(ロックあり/なし、異なる金額・受益者、異なる preimage)を観測した場合、当該観測は成立させずWATCHER_EQUIVOCATIONで拒否し、WatcherEquivocationDetectedを証跡化したうえで CASE へ収束させる(本書 §10.10)。当該外部イベントの終端化は保留される。定足数と equivocation の判定は一箇所に置く【規範】 :「鍵ではなく operator で数える/不一致は一致でない」という判定は、Watcher 観測と Attestation(
30_internal_design.md§11.2-c のmin_attester_quorum)で同一ポリシーを共有する。二つの経路が別々の判定を持つと、片方だけが緩む形の劣化が起きる。
既定値はレール別(規範):
onchain_min_watchersは作成時に確定種別から決める——
onchain_chain_class='PUBLIC'(確率的確定)は 2、PRIVATE/PERMISSIONED(決定的確定)は1。明示の
cross_chain.min_watchersは常に優先する(src/zc/lanes/htlc/create.ts)。reorg し得る解放を単独の Watcher が申告する構成が最も弱いため、そこにだけ既定で定足数を課す。
列の DDL 既定(1)はこの経路を通らない行のための保険であって、既定構成の水準ではない。
未分類の脚は作らせない(規範):
cross_chainを指定する要求にonchain_chain_classが無ければ
ONCHAIN_CHAIN_CLASS_REQUIREDで拒否する。既定値が確定種別から導かれる以上、種別を欠いた脚は最弱の既定に落ちるので、既定を選ばせるより宣言を求めるほうが安全である。
列の DDL 既定(1)と NULL 許容は、この経路を通らない既存行のために残る。
未充足:Watcher の独立性(鍵・運用組織・ネットワーク・チェーンノード)は制度側の
要件であり、系として検証していない——定足数は「相異なる operator」で数えるが、相関障害には無力である(本書 §7.7.3)。
10_requirements.md§8.5 の要件 S-4 に残るのはこの 1 点で、「機構が無い」でも「既定が 1 である」でもない。
30_internal_design.md第10章 Roadmap で追跡する。
7.7.3 本番拡大の前提(規範TODO:機構ではなく運用条件)
§7.7.2 の機構は規範として固定済みであるため、残る課題は機構の新設ではなく、その有効化と独立性の担保である。
- 確定種別の権威 :指定の必須化そのものは §7.7.2 の規範に含む(
ONCHAIN_CHAIN_CLASS_REQUIRED)。制度側に残るのは、どのsourceをどの種別とみなすかの権威(チェーン登録)で、種別は要求者の申告のまま検証されない(30_internal_design.md§10.3)。閾値の決定・変更は10_requirements.md§3.2.7(HIGH_VALUE 閾値のガバナンス)と同じ 4 眼の制度行為として扱う。 - Watcher 独立性要件 :鍵・運用組織・ネットワーク・チェーンノードの独立を参加要件(第14章)として規定する。定足数は「相異なる operator」で数えるが、operator が異なっても同一のチェーンノード/同一のクラウドリージョンに相乗りしていれば、相関障害(同一ノード障害で全 Watcher 沈黙・同一ノードの誤情報で全 Watcher が同時に誤観測)に対して k-of-n は無力である。 独立性は制度で担保するほかない。
- 接続認定試験への組込み :上記独立性の申告を、
10_requirements.md§7.2.7.2 の参加行接続認定試験(certification suite)で実地検証する対象に加える。
7.7.4 可用性・ライブネスと安全側縮退(規範)
- ライブネス障害=安全側に倒れる :解放観測が届かない場合、外側 ZC timelock の満了で
cancelHtlc→H 解放→refund となり、資金は失われない(recordOnchainFulfillmentの timelock 背骨)。すなわち Watcher 沈黙は「取引の失敗(refund)」であって「資金喪失」ではない。 - Watcher ライブネス SLO :にもかかわらず沈黙は取引失敗率を上げるため、観測到達遅延を SLI とし、SLO 逸脱を自動エスカレーション(第10章 10.9 SLA・障害対応Runbook)に接続する。timelock 余裕(
onchain_timelockと外側timelockの差)は観測 SLO を吸収できる値に設定する。 - 深い reorg の巻き戻しは未設計(残存リスク) :確認深度ゲートは「浅い解放を終端化しない」ことで事前の reorg を防ぐが、深度充足後に確定済み解放が reorg で消えた場合の事後 unwind 経路は現状無い。発生時は Reversal(別取引、
10_requirements.md§4.3 取消・組戻し・救済)+ CASE で人手収束する暫定運用とし、自動 unwind の要否・方式は制度合意の上で別途設計する。
7.7.5 鍵侵害時の手当(規範)
- KeyRegistry の失効は非遡及(失効前に検証成立した観測は有効のまま。
10_requirements.md§3.3.4-3)。したがって Watcher 鍵の侵害は、失効操作だけでは既に成立した観測を巻き戻さない。 - k-of-n による構造的緩和 :
onchain_min_watchers >= 2が有効な脚では、単一鍵の侵害だけでは終端化に至らない(相異なる運用主体の一致が必要)。逆に 既定のonchain_min_watchers = 1で運用している脚は、鍵 1 本の侵害がそのまま不正終端化に直結する——これが §7.7.3 で既定値の引き上げを本番前提に置く理由である。 - 手順 :侵害判明時の初動・影響範囲確定・救済は §10.9.3.5 Runbook に規定する(影響調査は
idx_watcher_observation_keyにより鍵別に追跡可能)。
設計の一貫性(§7.6 との関係):Watcher は「事実の署名付き観測」に権限を限定し、決済の確定権は ZC 側の hashlock 一致・深度ゲート・timelock 背骨が握る。これにより、§7.6 が忌避する「外部主体が業務の成否を裁定し責任分界が崩れる」事態を回避しつつ、クロスチェーン確定の観測という不可避の外部依存を、限定された・監査可能な・安全側に縮退する形で取り込む。
第8章 監査・ログ・証憑設計
要旨
本章では、Decision/Execution/運用操作を一貫した証跡連鎖として残し、後から誰でも検証できる監査設計を示す。Finality LogとWORM保全、Proof参照の粒度、照会と監査が同じ根拠に収束するための要件を固定する。
8.1 監査設計の原則(規範)
- 後日「なぜその判断か」を再現できること を必須とする。
- 決済は紛争・誤作動・監督対応の対象となるため、監査が弱い設計は必ず事務が破綻し、結果としてコストが跳ね上がる。
8.2 Finality Log(規範)
- Decision(DecideToSettle/Cancel)、H予約/凍結/解放、Execution確認(a/b)、運用操作(cap変更、停止/再開)を追記で記録。
- Read Modelはログから再構築可能であること。
8.2.1 ハッシュチェーン(規範:改ざん検知)
設計規範
FinalityLog は WORM(追記専用)かつ tamper-evident。同一 chain 内の各エントリは SHA-256 で直前エントリの
entry_hashを参照する。chain identifier はCOALESCE(txid, gtid, 'GLOBAL')。
アルゴリズム:
SHA-256 hash-chain v2(algorithmフィールドで明示)entry_hash の決定式:
entry_hash = SHA-256( prev_hash | log_id | txid | gtid | event_type | state_from | state_to | payload_json | event_seq | occurred_at )パイプ
|連結。フィールド順は契約(プロトコル変更不可、必要時は v3 として別アルゴリズム名で導入)。prev_hash: 同 chain の直前エントリの
entry_hash。新規チェーン先頭は文字列'GENESIS'。書込み原子性(規範): 状態 CAS UPDATE と FinalityLog INSERT は同一
db.batch()で発行する。INSERT はchanges() > 0ガード付きの条件付き INSERT で、CAS に勝った呼び出しのみがログを書く。バッチ内例外は両方ロールバック。「状態だけ進んで監査ログが残らない」窓は存在しない。実装:src/zc/lanes/_helpers.ts#transitionWithLogおよびsrc/zc/orchestrator/finality.ts。event_seq の単調性(規範):
FinalitySeq.next_seqをUPDATE ... RETURNINGでアトミック増分し、その戻り値をevent_seqとして割当てる(src/zc/orchestrator/finality.ts#allocateEventSeq)。旧方式(Date.now()*1000 + random+ UNIQUE 衝突 retry)は廃止。シード行(id=1)が未投入の環境ではMAX(event_seq)+1を defensive bootstrap として返す。- 全体 UNIQUE 索引
idx_fl_event_seq_uniqueは belt-and-braces(カウンタが冪等に動かない事象に対する保険)であり、主たる単調性保証ではない。
- 全体 UNIQUE 索引
検証 API:
GET /api/transactions/:txid/verify— TX チェーン検証GET /api/gtid/:gtid/verify— GTID チェーン検証GET /api/transactions/:txid/explain— 検証結果を timeline と一緒に返す(運用での標準)
break_reason 区分(検証失敗の理由):
LEGACY_UNCHAINED_ENTRY— チェーン導入前の旧データ(entry_hash IS NULL、src/zc/finality/finality_chain.ts)PREV_HASH_MISMATCH— チェーン分岐(部分 UNIQUE 索引idx_fl_chain_prev_hash/idx_fl_gtid_chain_prev_hashが事前に防ぐ)ENTRY_HASH_MISMATCH— 内容書き換え(DB 直接編集等)
並列書込みの安全性(規範): 同 chain への並列 INSERT は部分 UNIQUE
索引(idx_fl_chain_prev_hash:TX チェーン用 /idx_fl_gtid_chain_prev_hash:GTID 専用チェーン用)で
分岐を防ぐ。負けたワーカーは UNIQUE エラーを受け取り、writeFinalityLog
の retry ループ(最大 5 回)で chain tip を再読し追従する。transitionWithLog経路では条件付き INSERT (changes() > 0ガード) と
バッチの implicit transaction が retry 自体を不要にする —writeFinalityLog
の retry は単独 INSERT 経路(GTID 系のGtidRegistered等、CAS UPDATE を
伴わないログ)向けに残されている。
8.2.2 説明可能性 API(規範)
GET /api/transactions/:txid/explain: FinalityLog の各エントリに、日本語のreasonとactors(ZC | PAYER_BANK | PAYEE_BANK | CUSTOMER | IGS)を付与した timeline と、integrity.chain_verifiedを含めて返す。監査性は「別エンドポイント呼び出し」ではなく「読み出しに同梱する」。GET /api/transactions/:txid/story: 同データを「ナラティブ段落 + Mermaid sequenceDiagram + 健全性(OK / WATCH / STUCK / TERMINAL)」として返す。窓口・コールセンターが個別 TX を画面で確認する用途。- 実装:
src/zc/query/explain.ts,src/zc/query/story.ts,src/zc/finality/finality_chain.ts。
8.3 Proof参照(bank_proof_ref)(規範)
- a/b/Lock/Refund/外部決済完了/返し決済/取消 等の重要イベントは、必ずProof参照で追跡可能。
- 証憑の正(真正の発行主体)は銀行 。ただし参照可用性・監査性のため、ZC側で複製/WORM保全を許容(運用で確定)。
8.4 金額説明可能性(規範)
- amount_components(内訳)、calculation_version(算定ロジック版)、trace_digest(改ざん検知)を保持。
- gtidの場合は total_amount と leg列挙の整合を検証する。
8.5 運用イベントの監査(規範)
- 受付制限、cap変更、circuit breaker、強制停止/再開、誤記録訂正等は、必ず evidence_ref(根拠)と承認者を付してログ化する。
- 「誰が、なぜ、いつ」操作したかが対外説明・監督の主戦場となるため、ここは機能以上に重要。
第9章 障害時・災害時の動作
要旨
本章では、障害・災害時に「何を止め、何を続け、何を示すか」を状態遷移として定義する。Read-only縮退、再送嵐防止、DNS_HOLD/IGS結果の再照合、顧客表示での混同禁止など、危機時に説明可能性を失わないための固定点を整理する。
9.1 障害設計の基本(規範)
- 「止めない」ではなく「誤らない(誤決定しない)」を優先する。
- 曖昧な状態は「保留(SUSPEND)+CASE」で収束させ、後から説明可能にする。
9.2 多拠点障害(地域喪失)
- 任意の1地域喪失でも過半数が成立する配置を前提。
- 過半数に到達できない場合はRead-onlyへ遷移し、全レーンにおいて新規受付を
REJECT_UNAVAILABLE(またはREJECT_READONLY)として拒否する。 - 参加行は、Gateway/Edge側でリトライ制御(Exponential Backoff)を行うか、復旧後に再送する責務を負う。
- ZCは 永続化できない要求を内部キューに保持しない(「保留」で抱えない)。
9.3 メッセージ基盤障害(Kafka/MQ)
- 重複・順序乱れ・遅延は想定内。第5章の冪等・seqで収束。
- DLQは必ずCASEへ接続し、人手介入の入口を固定。
9.4 DNS(Daily Netting Settlement)とDNS HOLD(清算未完了による中断:内部要因は残高不足等)
9.4.1 DNSとは何か(略語・目的)
- DNS = Daily Netting Settlement(日次ネット清算) 。参加主体間で発生した多数の取引を当日内に集計し、 純支払/純受取(ネットポジション) に圧縮して清算する方式。
- 目的は (1) 清算回数の削減による低コスト化、(2) 参加主体の資金効率(流動性消費の抑制)、(3) 事務運用の整合(当日締め)である。
9.4.1.1 DNSサイクルと元取引の関係(規範:混同禁止)
DNSは 参加主体間の精算サイクル であり、原則として 元取引(txid/gtid)の完了状態 とは独立である。
ただし、 高額即時レーン(IGS) のみは例外で、清算が元取引の完了条件に組み込まれる。
高額即時レーン(IGS) :
- IGS清算(即時グロス清算)は 元取引の一部 として実行される
- 規範順序: a_HV成立 → IGS確定 → b成立
- IGS HOLD時:元取引は
SUSPENDED(b未成立)となり、顧客視点でも「未完了」
通常レーン(Express/Standard/Bulk/RTP/HTLC/gtid) :
- DNS清算は 元取引の完了後 に実行される参加主体間精算である
- 規範順序: b成立 → 元取引SETTLED → DNS清算
- H_lockedの解放はDNS清算完了(
DNS_CYCLE_SETTLED)で行う(b成立では解放しない) - DNS HOLD時:元取引は既に
SETTLEDであり、顧客視点で「未完了」にしてはならない
設計規範(顧客への影響説明) :通常レーンのDNS HOLDは「銀行間の精算が遅れている」状態であり、「取引が未完了」ではない。元取引の完了(b成立)と銀行間精算(DNS)を混同しないこと。
9.4.2 DNSサイクル(ZCが初回Kick、中央銀行が内部協調清算を実行)
DNSは、参加主体間で発生した多数の取引を束ね、 中央銀行内部で「全負け→全勝」へ資金を配賦する協調清算(内部gtid相当) として実行される。
このため、 勝ち参加主体だけを先に確定させる(部分確定) ことは原理的に成立しない(サイクル全体で確定/保留が決まる)。
ZCは営業日ごとにDNSサイクルを管理し、カットオフ到来時に 初回Kick を行う。
資金手当て完了の検知や再処理の最速主体は中央銀行であるため、 HOLD解除後の再処理(再Kick相当)は中央銀行側が自動で実行 し、ZCは結果通知を状態化する。
日内複数回カットオフ(24/365 への拡張)
カットオフは日次1回に限らない。
runIntradayDnsCutoff(POST /internal/dns/intraday-cutoff)が現 OPEN ウィンドウを kick+settle して直ちに次の正規形サイクルDNS-{CCY}-YYYYMMDD-NN(NN+1)を開き、新規取引を新ウィンドウへ振り向ける。これにより「日次1回 → 日内複数回 → ローリング」へ段階的に拡張できる。settle が HOLD(ショートフォール)となっても次ウィンドウは開き、受付は継続する(保留されるのは資金であってレールではない)。各カットオフはDnsIntradayCutoffで証跡化する。バリューデート・カットオフの意味論、休日・タイムゾーンまたぎの確定日、ネット債務者手当てプロトコルへの影響は制度設計側の論点として残す。
sequenceDiagram
autonumber
participant Bank as Participant (Payer/Payee)
participant ZC as ZC (Lane Orchestrator)
participant RM as Read Model
participant CB as 中央銀行(DNS清算サービス)
Bank->>ZC: txid/gtid flows (at-least-once)
ZC->>ZC: Netting aggregation (business_date)
ZC->>RM: Publish provisional net positions
Note over ZC: Cutoff reached (Kick: initial)
ZC->>CB: DNSSettlementRequest(cycle_id, business_date, net_positions, digest, kick_id)
CB-->>ZC: DNSSettlementResult(SETTLED or HOLD)
ZC->>RM: Publish DNS cycle status (official)
ZC-->>Bank: DnsCycleStatusUpdated(public)
alt HOLD (liquidity shortfall)
Note over CB: Market funding / Lending / Collateral
CB->>CB: Auto re-process (re-kick equivalent)
CB-->>ZC: DNSSettlementResult(RESUMED/SETTLED)
ZC->>RM: Publish updated status (official)
end
opt Exceptional (exclude bank / resolution)
CB-->>ZC: DNSSettlementResult(RECALC_EXCLUDING_BANK)
ZC->>RM: Publish recalculated cycle status (official)
end
- ZC→中央銀行 への依頼は、参加主体別ネットポジション(純支払/純受取)と根拠(digest/参照証跡)を添えて行う。
- DNS清算結果(SETTLED/HOLD/RECALC等)は、ZCがFinality Logに証跡化し、Read Modelへ反映する。
9.4.3 DNS HOLD(中断)の実体と扱い(規範)
設計規範(DNS HOLD=重大事象の扱い)
DNS HOLDは、参加主体の資金手当て(市場調達/貸付/担保)を要する重大事象であり、当局・中央銀行・ZC・資金不足を起こした負け参加主体で緊急連絡(電話/会議)を開始する。
ただし、緊急会議の対象は「資金不足を起こした負け参加主体のみ」であり、その他参加主体は公式ステータス/公式発表に従う(原因行の推測・照会を誘発しない)。
- HOLDの実体 : 中央銀行側に参加主体が預けている当座預金(預け金)残高不足 等により、清算が完了できない状態として表現する。
- 原則は即日解消 :負け参加主体は市場調達・貸付・担保等により当日中の解消を目指し、中央銀行は解消検知後に自動再処理する。
- 翌日持越しは例外 :当日解消不能(=貸付不可等)に至る場合、
- 中央銀行は当該参加主体をDNSサイクルから除外し、精算額を再計算(RECALC_EXCLUDING_BANK)
- 当該参加主体は前日から継続するH(担保拘束)を維持し、担保処分・破綻対応フェーズへ接続する
- 破綻対応フェーズでは、公的資金供給等により最終精算(H精算)へ収束させる
- 顧客要望への対応 :HOLD中に“決定をなかったことにする取消”は監査・顧客対応を破綻させるため禁止する。顧客の「やめたい」要望には、取消ではなく救済(Return/Reversal)で応答し、
CUSTOMER_CANCEL_REQUESTEDを状態化して次サイクルの救済へ接続する。
9.4.4 事務必須のDNS照会(当日勝ち負けの把握)
参加主体は当日DNSについて、以下を Query API で取得できることを規範とする(事務が回るための必須要件)。
(A) 全参加主体(公式ステータスに一致)
GET /api/dns/{business_date}/position(応答の項目名は32_api_contracts.md § GET /api/dns/:business_date/positionを正とする)- ネットポジション(+受取/−支払)
- グロス送信/グロス受信(参考)
as_of/watermark(鮮度)
GET /api/dns/{business_date}/status- サイクル状態(値域は
src/types/states.ts#DnsStateを正とする:OPEN/KICKED/SETTLED/HOLD_ACTIVE。サイクル未生成時の特例値NOT_STARTEDを含む応答契約は32_api_contracts.md § GET /api/dns/:business_date/status) hold_reasonの類型のみ(下記の規範。不足額の内訳は載せない)public_message_id(公式発表と突合するID)next_action_hint(待機・再照会時刻等)
- サイクル状態(値域は
規範(値域の混同禁止) :
RECALC_EXCLUDING_BANKはサイクル状態ではない。これは中央銀行が返す清算結果(DNSSettlementResult)の種別であり(§9.4.2 のシーケンス図・§9.4.3)、ZC 側のサイクル状態は上記 4 値に閉じる。除外再計算の結果は、再計算後のネットポジションと新しいサイクル状態(KICKED/SETTLED/HOLD_ACTIVE)として観測する。
規範 :このレベルの照会は、公式発表・公式ステータスと完全一致させる。原因行の特定や不足額など、風評・市場混乱を誘発しうる情報は返さない。
規範(
hold_reasonの粒度):hold_reasonは「なぜ止まっているか」の類型(現行の実値はBOJ_INSUFFICIENT_FUNDS)までとし、原因行の特定情報と不足額の内訳を全参加主体向けのstatusに載せてはならない。当事者はこれらを §9.4.4 (B) のhold_detail(閉域)から取得する。public_message_idは HOLD 突入時に採番し(DNS_HOLD_{business_date})、解消・清算完了で NULL に戻す。注意(
DnsCycles.hold_reason列はスカラーではない):同列は{reason, shortfalls}の JSON であり、shortfalls(参加行別の不足額)を含む。すなわち列そのものが閉域情報を抱えているため、status応答へ列の値をそのまま透過してはならない——載せてよいのはreasonの類型だけである(31_schema.md § DnsCycles)。
未充足:
as_of/watermark(鮮度)はネットポジション照会に付与されず、next_action_hintは取引照会(GET /api/transactions/:txid)にのみ付く——いずれも DNS サイクル照会には無い(応答の実形は32_api_contracts.md § GET /api/dns/:business_date/position)。本節の規範は「事務が回るための必須要件」として書かれているため、この差分を充足済みとして扱ってはならない。30_internal_design.md第10章 Roadmap で追跡する。
(B) 資金不足を起こした負け参加主体(閉域:当局/中央銀行/ZCと共有)
GET /api/dns/{business_date}/hold_detail(閉域認可がある場合のみ。契約は32_api_contracts.md § GET /api/dns/:business_date/hold_detail)shortfall_amount(不足額)collateral_call_amount(必要担保)recommended_actions(市場調達/貸付申請/担保差入)contact_channel(緊急連絡経路)
ZCは照会結果に next_action_hint を必ず付与し、オペレーションを定型化する。
規範(閉域統制:本照会に固有の 4 点) 契約は
32_api_contracts.md § GET /api/dns/:business_date/hold_detail。
- 拒否は常に 404:目的コード欠落/主体不明/当日 HOLD 無し/当事者でない参加行——4 つすべてが同一の 404 本文を返さなければならない。
403は「無い」と「見せない」を区別してしまい、「あの行は今日ショートしているのか」という取り付けの引き金そのものを答えてしまう。- 目的コードが無ければ実時間で遮断する(
10_requirements.md§3.3.2.2.1)。事後監査ではなく遮断が規範であり(同 §3.3.2.2.1.1-2)、DataAccessViolationDetectedを GLOBAL チェーンへ記録する。- 許可した読み取りも記録する(
ClosedDomainAccessGranted)。原因行の特定情報と不足額は ZC が提供する中で最も機微であり、「誰が・どの目的で・いつ見たか」を残す。- 正当な非当事者を違反として記録しない:目的コード付きで照会した無関係の参加行は 404 を受けるが、違反ログには載せない——本物の違反が埋もれるため。
9.4.4.1 DNS HOLD時の照会:レーン別の応答規範(規範)
DNS HOLDは「精算サイクル」の状態であるため、 元取引(txid/gtid)の応答 はレーンにより変わる。
(A) 高額即時レーン(IGS)でのHOLD(元取引がSUSPENDED)
GET /api/transactions/{txid}state: SUSPENDEDreason_code: IGS_FAILED(値域は32_api_contracts.md § 状態 reason_code)decision: DECIDED_TO_SETTLE(決定済み)execution.a: OK(a_HV成立済み)execution.b: NONE(入金未了)next_action_hint: WAIT(next_action_hintは窓口の定型文言に 1:1 対応する閉じた 4 値であり、事象ごとに新しい値を作らない——値域は30_internal_design.md§13.6 が正)public_message_id: IGS_HOLD_{business_date}(公式発表と突合。IGS リングフェンスで止まった HIGH_VALUE にのみ付与する)
規範(HOLD と不成立を
reason_codeで見分けようとしない)中銀決済が HOLD(一時的な流動性不足。再試行で解消しうる=「待てば済む」)で返っても、
不成立(=「待っても済まない」)で返っても、取引に付く状態
reason_codeは同一の
IGS_FAILEDである。両者は顧客に対して正反対の説明を要求するにもかかわらず、
reason_codeはその区別を運ばない。したがって窓口の分岐は
external_settlementフィールドで行う(規範)——
{status, retriable}を返し、retriable=true(HOLD/TIMEOUT)なら「待てば進みうる」、
false(FAILED)なら「待っても済まない」である。reason_codeを見て「失敗した」と説明してはならない。
retriableを ZC 側で導出する理由(規範):清算状態から「待って意味があるか」への写像はスキームルールであり、参加行ごとに実装させると危機時の答えを取り違える機会が参加行の数だけ
生まれる。導出は一箇所に置く。中銀の生の失敗理由は運ばない(規範):
IgsRequests.failed_reasonは相手方の不足を名指しし得るため、閉域(§9.4.4 (B))に留める。本応答が返すのは類型(
status)と分岐(retriable)までである。
public_message_idは引き続き公式発表テンプレとの突合キーとして併記されるが、窓口の分岐根拠ではなくなった——「無関係な事実からの推論」に危機時の説明を賭けない。
(B) 通常レーンのDNS HOLD(元取引はSETTLED)
GET /api/transactions/{txid}state: SETTLED(完了)execution.a: OK/execution.b: OKdns_settlement_status: HOLD_ACTIVE(参考情報として付与可)next_action_hint: WAITかつnext_retry_at: null(終端であること自体はstate: SETTLEDが示す。「完了」を表す専用の hint 値は作らない——値域は30_internal_design.md§13.6 が正)
規範 :通常レーンの取引について、DNS HOLDを理由に「取引未完了」と表示してはならない。
9.4.4.2 公開メッセージIDと推奨表示テンプレ(参加主体→顧客)
ZCは顧客へ直接表示せず、参加主体に public_message_id を提示する。
参加主体は顧客向け表示において、 原因(資金不足等)を断定する表現を避け 、公式発表に整合させる。
規範(ZC が担保する範囲の限界) サイクル状態には
DNS_HOLD_{business_date}、IGS で止まった HIGH_VALUE にはIGS_HOLD_{business_date}が付き、参加主体はこの ID に対応する事前承認テンプレへ顧客表示を限定する。テンプレ本文そのものの承認・配布は制度側の手続であり、ZC が担保するのは「どのテンプレを使うべきか」の機械可読な指示までである。この線を越えて ZC が顧客向け文面を配信することはしない。
推奨テンプレ(例)
- 「○○銀行への振込は、障害により一時中断しております。」
- 「ただいま処理状況を確認しております。しばらくしてから再度ご確認ください。」
- 「本件は決済処理の都合により遅延しております。完了次第お知らせします。」
禁止例
- 「○○銀行は資金不足です」
- 「○○銀行が負けています」
規範 :顧客向け表示は、原因行の推測・取り付け騒ぎ等を誘発しうる文言を避ける。
9.5 高額即時レーン結果の再照合
- ExternalSettlement Adapterは結果(完了/未了)を再取得可能であること。
- ZCはRead Modelを再構築し、取引ごとに「決定済み/実施未確定」の説明状態を維持する。
- 返し決済(高額即時レーン)は別ID・別証跡で記録し、元取引の履歴を書き換えない。
9.6 RTO/RPO(提出用の枠)
- 数値は制度・RFPで確定するが、枠として以下を固定する:
- RPO:Finality LogはRaftコミットを唯一正とし、コミット済みは消えない。
- RTO:Read Modelはログから再構築できることをもって復旧要件を満たす。