Corda 4.11 enterpriseにて追加されたTwo Phase Finalityの詳細について把握でき、次回以降の記事で解説するLedger Recoveryを理解する上で重要な内容となります。
はじめに
Corda 4.11 enterpriseでは、リカバリー関連の処理が、これまでのversionから大きくアップデートされ、複数の新機能が追加されました。Corda 4.11 enterpriseで導入された新機能に関する記事は、全3部構成(Two Phase Finality, Finality Recovery Flow, Ledger Recovery Flow)を予定しており、本記事は第一弾として、Two Phase Finality (略称:2PF)をご紹介したいと思います。
2PFの導入により、以前のversionから採用されていたトランザクションのファイナライズの構成が細分化され、第二弾でご紹介する台帳のリカバリー機能を実現する上で、大きな役割を担っています。
なお、2PFの機能は、ビルドインでCorda 4.11 enterpriseのFinality Flowに組み込まれているため、2PFの導入に伴い、ユーザー側で新たに処理を追加する必要はありません。
既存ファイナリティ構成のご説明
2PFの特徴をご理解いただく上で、重要となるのが既存ファイナリティの理解になります。
そもそも、Cordaの文脈でファイナリティとは何を指すのでしょうか?
ファイナリティとは、NotaryによるNotarizing(二重消費チェック)、 Recording (トランザクションを利害関係者の台帳に保存する行為)やSharing (全ての参加者にトランザクションを配布する行為)をひっくるめて指した言葉になります。
もう少し技術的な文脈に起こすと…
Flowの送信者は、ビルトインの機能であるFinalityFlow
を用いて、トランザクションのファイナライズを行います
- トランザクションを選択したNotaryに送信し、二重消費チェック
- トランザクションをローカルのvaultに記録
- 記録のために、Signed transactionを他の全ての参加者に送信する
さて、上記のファイナリティの処理を図に起こすと以下のようになります。
一見、よくできたファイナリティの構成のように思えますが、実は障害発生を想定すると、リカバリーの操作性に関する難点が存在します。 ※ここでの障害は、ネットワーク障害やノードが利用停止状況に陥る事を指します。:
flowの受信側(上図のBさん)は、ファイナリティが完了して、初めてトランザクションを受け取ります。したがって、Bさんはリカバリーに必要な材料を保持しない構成のため、ファイナリティ処理のリカバリーの起点は、常にflowの送信側(上図のAさん)に制限されるという制約が存在しました。
故に、Corda 4.10以前では、障害時のリカバリー処理に関して、柔軟性に欠けることが課題として挙げられていました。
新規手法Two Phase Finalityの登場
既存手法の問題点に対処するために、Corda 4.11 enterpriseからTwo Phase Finalityという機能が追加され、ファイナリティ処理を2段階(Two Phase)に分割する仕様へとなりました。
2PFの構成は、以下の図の通りです。
筆者が加えたオレンジの点線を境に、ファイナリティ処理がTwo Phaseになっていることが分かります。解説のため、点線の上側はPhase 1、点線の下側をPhase 2としたいと思います。(Cordaのドキュメント内ではPhase分けされていません。)
Phase 1: Notarization前のトランザクションを共有
まずPhase 1の解説になります。
flowの送信者(図中のAさん)が署名検証を行った後に、Record
という処理に入り、図中SendTxnFlow, ReceiveTxnFlow等を通して、Notarization前のトランザクションを利害関係者間で配布し、各DBに保存する処理がPhase 1になります。
Phase 1により、Notarization前の段階で、トランザクションの利害関係者が、トランザクション情報を保持するようになりました。
共有されるトランザクションの実体は?
Corda 4.11 enterpriseのドキュメントでは、Phase 1にて共有されるトランザクションの実体はメタデータであり、Distribution record
という形で明記されています
その情報は、flowの送信者であればDB内のnode_sender_distr_recs
というテーブルに、受信者であれば、node_receiver_distr_recs
というテーブルに保存されます
以下に例として、JakeというPartyが自身のDB内に保持するnode_sender_distr_recs
を添付します。
トランザクションのid、利害関係者のハッシュ値、stateの保存状況に関するフィールドが存在し、これらの情報はリカバリー処理に使用されます。
postgres=# select * from jake_ledger_recovery.node_sender_distr_recs;
timestamp | timestamp_discriminator | transaction_id | peer_party_id | sender_states_to_record | receiver_states_to_record
---------------------+-------------------------+------------------------------------------------------------------+------------------------------------------------------------------+-------------------------+---------------------------
2025-04-16 06:07:40 | 0 | 2B1E803E900A15817BBF880C4D2DDE6B5486F17E5AE7F647CB52C7740E8B3374 | 9621CE754A8C1CB81D156A762FB1911E31F9C3AD24C9B7C5112EBC6DFC11EAB9 | 2 | 2
2025-04-18 05:41:27 | 0 | 1194E3DD9BCE39D13C86E91C50203D55E993D6662FC0466C4F2330D410953967 | 9621CE754A8C1CB81D156A762FB1911E31F9C3AD24C9B7C5112EBC6DFC11EAB9 | 2 | 2
2025-04-18 06:43:27 | 1 | 83C103FD822EC495DEB62267204C402497EA82AF8BCCA7DFE36D6C41F6E4E8D1 | 9621CE754A8C1CB81D156A762FB1911E31F9C3AD24C9B7C5112EBC6DFC11EAB9 | 2 | 2
2025-04-18 07:39:51 | 2 | 2A8B8DF577816A618C270C8B0E2637747DC21904161868276E1DC646F86908F6 | 9621CE754A8C1CB81D156A762FB1911E31F9C3AD24C9B7C5112EBC6DFC11EAB9 | 2 | 2
Phase 2: トランザクションのNotarizaction, Recording, Sharing
Phase 2に関しては、既存のファイナリティ手法と何ら変わりません。
Notarization, Recording, Sharingのプロセスを経ることで、最終的にトランザクションの利害関係者が、Notarize済みのトランザクションを手元に保持することになります。
Two Phase Finalityの特徴
既存のファイナリティ処理とは異なり、Phase 1の時点で、トランザクションの利害関係者が、トランザクションのメタデータを持ちます。故に、障害が発生した場合でも、flowの送信者と受信者の両方がリカバリー処理の始点となることができます。
Two Phase Finalityでは、IN_FLIGHTとい新しいトランザクションのstatusが導入され、ファイナリティ処理が開始した段階で、statusはUNVERIFIED
からIN_FLIGHT
へ遷移します。また、ファイナリティ処理の終了後には、statusはVERIFIED
となります。
https://docs.r3.com/en/api-ref/corda/4.11/community/javadoc/net/corda/core/node/services/TransactionStatus.html
Two Phase Finality導入による影響と最適化手法
さて、2PFの導入によって、リカバリー処理に柔軟性が追加されました。しかし、既存手法と比較した時、2PFがもたらすオーバーヘッドはどのくらいになるのでしょうか。
実際に、R3が計測を行ったところ、現金発行や支払いに関するCordappにおいて、2PFの導入により、最大15%の遅延増加と最大15%のスループット低下が観測されたそうです。
しかしながら、R3社の考えとしては、2PFがもたらす障害耐性やリカバリーの柔軟性は、導入障壁とのトレードオフとして得られる利点であり、実際にパフォーマンスを最適化する手法が2つほど提案されています。
1. ACKの無効化
先ほど、添付した2PFの図では、SendTxnFlowによるDistribution recordの共有を受けて、受信側がACK (受信確認)を送信しています。一般的に、ACKが返ってこないと、送信側で次の処理が開始できない仕様になっています。
取引やリカバリーに確実性が求められるユースケースでは、ACKの利活用は非常に有効です。一方で、テスト目的ならびに帯域の制限や速度を気にする場合は、ReceiverTransactionFlowのパラメータで、deferredAck = true
を設定することで、ACKを返答しないようにすることも可能です。ただし、defeeredAck = true
では確実性は保証されないので、ご注意ください。
2. Optimistic Finalisationプロトコルの実行
Optimistic Finalizationプロトコルの説明は少しわかりにくいため、例を用いて解説したいと思います。
仮定:flowの送信者はファイナライズが完了したが、受信者側はファイナライズが滞留状態=未完了である
- 送信者は、ファイナライズ済みのトランザクションで生成したstateを消費するトランザクションXを作成
- 送信者がトランザクションXのファイナライズ処理を開始し、受信側にトランザクションXを共有する
- 受信側は滞留状態にあるため、トランザクションXで消費されるstateを作成したトランザクションは未ファイナライズ状態である。
- 受信側は、トランザクションXをファイナライズすることで、滞留状態にあったファイナライズ処理を、自動的にファイナライズ済みとマークします
つまり、Optimistci Finalization プロトコルとは、あるトランザクションのファイナライズ処理が滞っている状況下において、その派生トランザクションを受信した場合、派生トランザクションのファイナライズ処理をもって、滞留していたファイナライズ処理を完了させる機能になります。
Optimistic Finalization プロトコルの利点
派生トランザクションのファイナライズによって、一つ前のトランザクションを自動承認し、ファイナライズ処理の手間と回数を減らすことが可能になります。
最後に
本記事では、Corda 4.11 enterpriseにて新たに追加されたTwo Phase Finalityの機能をご説明しました。
2PFの導入によって、ファイナリティ処理が2段階になり、Distribution Recordというメタデータの共有は、Cordaの障害耐性やリカバリー時の柔軟性を大幅に向上させる要素となりました
また、2PF自体は多少なりともオーバーヘッドの増加を招くものの、最適化によって導入の障壁を限りなく減らすが可能になります。
何よりも、2PFがもたらす障害耐性やリカバリーの柔軟性という利点は、導入による欠点を大きく勝る要素であると我々は考えております。
<ご質問・ご要望の例>
- Corda Portalの記事について質問したい
- ブロックチェーンを活用した新規事業を相談したい
- 企業でのブロックチェーン活用方法を教えて欲しい 等々
SBI R3 Japanインターン エンジニアリング部所属
Cordaの技術調査・検証や記事執筆など多岐に渡ります
趣味:英語学習(まだまだですが…)・テニス・映画鑑賞
イギリス大学院進学予定2025年9月~🇬🇧