はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、Subnetで参照されるP-Chainのブロックが本当に承認されたものであることを他のノードから証明付きで確認できる仕組みを提案しているACP75についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

ACP75は、あるブロックがコンセンサス(合意)によって承認されたことを第三者が確かめられる「AcceptanceProof(承認証明)」をプロトコルとして扱えるようにする提案です。 これにより、ピア(他ノード)が「そのブロックは本当にネットワークで承認されたのか」を暗号学的に確認でき、必要に応じて当該ブロックを取り寄せて自分のローカル状態を前進させられます。 結果として、Subnet上のブロック検証や進行性がP-Chainの同期遅延による影響を受けにくくなります。

動機

課題

AvalancheのSubnetはWarpメッセージング(Subnet間で任意の出来事を証明付きで伝える仕組み)を利用して様々なイベントの証明をやり取りできますが、「特定のブロックが承認されていること」をプロトコルレイヤーで標準的に扱えるようになると、Subnetの信頼性と柔軟性が向上します。 特に、PrimaryNetwork(P-Chainを含む基盤側)と各Subnetとの間でFaultIsolation(障害の分離)を強化できる点が重要です。 ここでのFaultIsolationとは、例えばP-Chainで新しいブロックの承認が遅延しても、Subnet側のブロック生成や検証処理が影響を受けずに継続できる仕組みを指します。

ProposerVMとP-Chain高の参照

SubnetではProposerVM(ソフトリーダー選出のためのVM)が使われ、P-Chainの特定のBlock Hightにおけるバリデータ集合をランダムに並べ替えて、ブロックプロデューサーの順序を決定します。 この仕組みのため、各バリデータは検証対象のSubnetブロックのヘッダに記載されたP-ChainのBlock Hightに対応するP-Chainブロックをすでに承認済みであることを前提に検証を行います。 もし参照先のP-Chainブロックを承認していなければ、「このSubnetブロックの提案者がその時点で正しい順番のプロデューサーであるか」を確認できません。

  • ヘッダが参照するP-Chainブロックがまだ承認されていない場合、そのSubnetブロックは不正(Invalid)として扱われます。
  • ただし、そのSubnetブロックが正しく提案(Virtuous)されたものであれば、バリデータは自分のP-Chain同期が進んだ時点で参照先のP-Chainブロックを承認でき、その後Subnetブロックも承認可能となります。

停滞リスク

問題は、複数のバリデータが最新のP-ChainのBlock Heightをそれぞれ異なる数値で認識している場合です。 極端なケースでは、Subnet全体で新しいブロックがまったく生成されなくなる可能性があります。 現実的には、多くのノードが少し前のP-ChainのBlock Heightを参照してSubnetブロックを提案するため、そのBlock Heightに対応するブロックがすでに承認済みである確率が高く、大きな停止はほとんど起こりません。

しかしこれは、「P-Chainで新しいブロックの承認が常に順調に進む」という前提に強く依存しています。 もしノードごとにP-Chainの承認が遅れ、その遅れ具合がばらばらだとSubnet全体でブロック検証が進まず、新しいブロック生成が止まる可能性が残ります。

AcceptanceProof(承認証明)

この課題を解決するために導入されるのがAcceptanceProofです。 これは「対象のP-Chainブロックがすでに合意により承認された」ことを、他のピアから提供される証明によって確認できる仕組みです。

動作の流れは次の通りです。

  1. バリデータがSubnetブロックを検証中に、参照しているP-Chainブロックをまだ承認していないと気づいた場合、そのP-Chainブロックと承認証明を任意のピアに要求します。 ここで「任意のピア」とは、必ずしもP-Chainの合意に参加しているノードでなくても構いません。
  2. 受け取った承認証明が正当であれば、そのバリデータは該当するP-Chainブロックをローカルに適用して同期を進め、続けてSubnetブロックの検証を正しく行えるようになります。
  3. この仕組みによって、ローカルでP-Chain合意に参加する必要もなく、P-Chainバリデータと直接通信する必要もありません。 結果として、必要な接続数やP2Pメッセージの負荷を減らし、P-Chainバリデータに集中する負担を軽減できます。

バリデータはP-ChainブロックAとBまでは承認済みですが、提案されたSubnetブロックが参照するP-ChainブロックCをまだ知らないため、Subnetブロックの検証に失敗しています。

SubnetブロックZが未知のP-ChainブロックCを参照しており、検証が進められない状況です。

バリデータがピアに対してP-ChainブロックBとCの本体および承認証明を要求しています。

バリデータが証明を検証してBとCを承認し、その結果SubnetブロックZの検証を完了できることを示しています。

仕様

GetAcceptanceSignatureRequest

以下は特定のブロックに対する署名をピアに要求するためのメッセージです。

diff
+ message GetAcceptanceSignatureRequest {
+   bytes chain_id = 1;
+   uint32 request_id = 2;
+   bytes block_id = 3;
+ }

このメッセージを送信することで、「指定したBlock IDについて、あなたが承認したという署名を返してください」というリクエストを相手に伝えます。 各フィールドの値は以下です。

フィールド名 型 説明
chain_id bytes どのチェーンに属するブロックかを識別するためのID。SubnetやP-Chainごとに異なる識別子を持ちます。
request_id uint32 複数のリクエストを同時に扱う場合にレスポンスと対応付けるための一意な番号。
block_id bytes 署名を要求する対象のBlock ID。ブロックハッシュなどが格納されます。

GetAcceptanceSignatureResponse

リクエストに対する応答として返されるのが GetAcceptanceSignatureResponse です。

diff
+ message GetAcceptanceSignatureResponse {
+   bytes chain_id = 1;
+   uint32 request_id = 2;
+   bytes bls_signature = 3;
+ }

このメッセージは、指定されたBlock IDが承認されている場合、その証拠となるBLS署名を返します。 承認されていない場合は、署名が空で返されます。 各フィールドの値は以下です。

フィールド名 型 説明
chain_id bytes リクエストと同じチェーンIDを返すことで、どのチェーンのブロックに関するレスポンスかを明確にします。
request_id uint32 リクエストと同じIDを返すことで、どのリクエストに対する応答かを対応付けます。
bls_signature bytes リクエストされたBlock IDに対して、ピアが自分のPrimary Networkに登録しているBLSステーキングキーで生成した署名。もしそのブロックをまだ承認していない場合は空になります。

BLS署名の利用理由

ここで利用される署名方式はBLS署名です。 BLS署名は、複数の署名をひとつにまとめられる「署名の集約」が可能で、効率的に検証できるのが特徴です。 この特性により、複数のバリデータによる承認をコンパクトに証明でき、ネットワーク負荷の削減や検証効率の向上につながります。

処理の流れ

  1. バリデータがSubnetブロックを検証中に、参照先のP-Chain Block IDをまだ承認していない場合、そのBlock IDに関する署名を求めて GetAcceptanceSignatureRequest をピアに送信します。
  2. ピアはそのブロックを承認していれば、Primary Networkに登録済みのBLSステーキングキーを用いて署名を生成し、GetAcceptanceSignatureResponse として返します。
  3. 承認していない場合は、署名フィールドが空のレスポンスを返します。
  4. リクエストを送信したバリデータは、この署名を検証することでブロックの承認を確認し、ローカル状態を前進させます。

セキュリティ

State Syncによる履歴の欠落

ノードは起動時にState Sync(状態同期)を利用することで、過去すべてのブロックをダウンロードせずに最新の状態を取得できます。 これは同期を高速化する便利な仕組みですが、問題は「P-Chainの完全な履歴を保持していない」ことにあります。

あるノードが新しいSubnetブロックを提案したとき、そのブロックが参照するP-Chainブロックが過去の履歴に含まれている場合、履歴を保持していないノードはその証明(ブロックチェーン上のつながり)を提供できません。

具体的には、他のノードがP-Chainの状態を高速で同期しようとしている(Fast-forward)とき、そのノードは「現在承認済みの先頭ブロック」から「追いつきたいブロック」までの完全な系列(Ancestry)を必要とします。 しかし、State Syncで起動したノードはこの系列を提供できないため、相手の同期を妨げてしまう可能性があります。

最低限の状態保持とリトライ

この問題に対しては「各ノードが最低限の最近の履歴を保持している」ことが前提とされています。 つまり、履歴を完全には持たなくても、ある程度の直近のブロック履歴さえあれば、リクエストしたノードはリトライを繰り返すことで最終的に必要な系列を持つノードに到達できるという考え方です。

重要なのは、1つのノードでも要求された系列を保持していれば、他のノードは最終的に追いつけるという点です。 そのため、完全な耐障害性はないものの、ネットワーク全体としては機能が維持されることが期待されます。

代替案:Stale Validator Setの利用

別のアプローチとして、「古いバリデータセット(Stale Validator Set)を使ってブロックの証明を検証する」という方法も考えられます。 これは、完全な履歴を持っていなくても、ある時点のバリデータ情報を使って証明を処理する仕組みです。

しかし、この方法はセキュリティ上の懸念が残ります。 具体的には、古いバリデータ情報が悪用されて不正な証明を通してしまう可能性があるため、設計を単純化できる一方でリスクを増やす結果になります。

最後に

今回は「Subnetで参照されるP-Chainのブロックが本当に承認されたものであることを他のノードから証明付きで確認できる仕組みを提案しているACP75」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!