はじめに

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

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

今回は「ERC7965によるクロスチェーンメッセージの暗号学的証明検証」についてまとめていきます。

ERC7786のクロスチェーンメッセージングに暗号学的証明による検証属性を追加し、外部バリデータに頼らずチェーン自身の合意メカニズムでメッセージの真正性を証明できる仕組みを提案しているERC7965についてまとめていきます!

以下にまとめられているものを解説しながらまとめていきます。

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

概要

ERC7965とは、ERC7786のクロスチェーンメッセージングゲートウェイに暗号学的証明(Cryptographic Proofs)の属性を追加する提案です。

ERC7786については以下の記事を参考にしてください。

ブロックチェーン同士がメッセージをやり取りするクロスチェーンメッセージングでは、「送信元チェーンから本当にそのメッセージが送られたのか」を検証する仕組みが必要です。 従来のプロトコルでは、外部のバリデータ(検証者)やマルチシグ(複数署名)に頼ってメッセージの正しさを保証していました。 しかしこの方法では、バリデータ自体が攻撃されたり停止したりすると、クロスチェーン通信全体が機能しなくなるリスクがあります。

ERC7965は、暗号学的証明を使うことでこの問題を解決します。 送信元チェーンのステートやトランザクション履歴をストレージ証明(Storage Proofs)などで直接検証するため、外部の信頼できる第三者が不要になります。 つまり、チェーン自身が持つ合意メカニズムと暗号学的コミットメントだけでメッセージの正当性を証明できるということです。

さらにこの提案では、メッセージの宛先を柔軟に指定できる「ブロードキャスト」の仕組みも標準化しています。 特定チェーンの全アドレス宛、同じ種類のチェーン全体宛、あるいはすべてのチェーンとアドレス宛という3段階のブロードキャストパターンを使い分けることで、オラクルの価格フィードやガバナンス通知、緊急停止命令など用途に応じた配信が可能になります。

動機

外部バリデータへの依存という課題

現在のクロスチェーンメッセージングプロトコルの多くは、メッセージの正しさを保証するために外部のバリデータ、マルチシグ(複数の鍵による署名)、または楽観的メカニズム(一定期間内に異議がなければ正しいとみなす方式)に依存しています。

これらの方式には共通の問題があります。 メッセージの検証にチェーン外部の信頼できる主体が必要になるため、その主体が攻撃を受けたり、サービスを停止したりすると、クロスチェーン通信全体が止まってしまいます。 また、バリデータの過半数が共謀すれば不正なメッセージを「正しい」と承認できてしまうリスクもあります。

暗号学的証明を使えば、チェーン自身が持つ合意メカニズムと暗号学的コミットメント(ブロックハッシュやストレージルートなど)を直接利用して検証できます。 外部の信頼前提が不要になるため、障害点が減り、セキュリティが向上します。

ブロードキャストの粒度不足

ERC7786では、受信者アドレスを「省略またはゼロ」にすることでブロードキャスト(一斉配信)ができると定義しています。 しかし、この仕組みは「全チェーン・全アドレスに送る」という1パターンしかありません。

実際のユースケースでは、もっと細かい制御が必要になります。 例えば、オラクルの価格フィードは特定のチェーンにだけ送りたい場合があります。 ガバナンスの投票結果はEVM互換チェーン全体に通知したいけれど、非EVMチェーンには不要かもしれません。 緊急停止命令はすべてのチェーンに届けたいでしょう。

この提案による解決策

ERC7965は、以下の柱でこれらの課題を解決します。

  • 暗号学的証明による検証 チェーンのステートやトランザクション履歴を直接証明することで、外部バリデータなしにメッセージの正当性を確認できます。
  • 段階的なブロードキャストパターン ERC7930の相互運用可能アドレス構造を活用し、アドレス・チェーンタイプ・ユニバーサルの3段階で宛先の粒度を制御できます。
  • マルチホップルーティング 直接の検証関係がないチェーン間でも、中継チェーンを経由して信頼の連鎖を構築し、メッセージを検証できます。
  • 既存インフラとの互換性 既存のERC7786ツールやインフラを修正なしでそのまま活用できます。 属性システムを拡張する形で設計されているため、新しいインターフェースを作り直す必要がありません。
  • 証明システム間の相互運用 共有のSettlementインフラや互換性のある証明システムを通じて、検証可能なステート関係を持つチェーン間であれば動作します。

仕様

ブロードキャストの仕組み

ERC7965のブロードキャストは、ERC7930の相互運用可能アドレス(Interoperable Address)構造を利用しています。

ERC7930については以下を参考にしてください。

https://eips.ethereum.org/EIPS/eip-7930 ERC7930のアドレスは「チェーンタイプ」、「チェーン参照値」、「アドレス」という3つのフィールドで構成されており、各フィールドの長さをゼロに設定することで「その範囲すべてに送る」という意味になります。

以下の3つのブロードキャストパターンが定義されています。

パターン ChainType ChainReference Address 用途例
アドレスブロードキャスト 指定あり 指定あり 長さ0 Arbitrum One上の全アドレスに価格フィードを配信
チェーンタイプブロードキャスト 指定あり 長さ0 長さ0 すべてのEVM互換チェーンにガバナンス通知
ユニバーサルブロードキャスト 0 長さ0 長さ0 全チェーン・全アドレスに緊急停止命令

以下の図は、3つのブロードキャストパターンの粒度の違いを示しています。

ブロードキャストパターン比較図

左から右に行くほどブロードキャストの範囲が広くなります。 ERC7930アドレスのどのフィールドを「長さ0」に設定するかで、配信先の粒度が変わります。

アドレスブロードキャスト

特定のチェーン上にあるすべてのアドレスにメッセージを送るパターンです。 ChainTypeとChainReferenceを指定し、AddressLengthを0に設定します。

例えば、Arbitrum One(チェーンID 42161)上のすべてのアドレスにメッセージを送る場合、チェーンタイプにeip155(EVMチェーンのnamespace)を、チェーン参照値に42161を設定し、アドレス部分を空にします。 オラクルが特定チェーンの全コントラクトに価格情報を配信する場面で使えます。

チェーンタイプブロードキャスト

同じ種類のチェーンすべてにメッセージを送るパターンです。 ChainTypeを指定し、ChainReferenceLengthとAddressLengthの両方を0に設定します。

例えば、eip155を指定すればすべてのEVM互換チェーンに送れます。 ガバナンスの提案結果をEVM互換チェーン全体に通知する場面で有効です。

ユニバーサルブロードキャスト

すべてのフィールドをゼロに設定することで、対応するすべてのチェーンとアドレスにメッセージを送ります。 これはERC7786で定義されている基本的なブロードキャストと同じ動作です。 緊急停止命令など、あらゆるインフラに届ける必要があるメッセージに使います。

ブロードキャストメッセージの受信者は、メッセージの送信元と内容を自身のセキュリティ要件に従って検証する必要があります。

暗号学的証明の属性

ERC7965は、ERC7786の属性(attributes)システムを使って3つの暗号学的証明属性を定義しています。 ゲートウェイ(メッセージの中継役)は、サポートする属性に対してsupportsAttributeが呼ばれた時にtrueを返す必要があります。

属性名 必須/任意 役割
route 必須 検証経路の指定。マルチホップに対応
inclusionProof 必須 メッセージが送信元チェーンのステートに含まれていることの証明
targetBlock 任意 メッセージがコミットされたブロック番号

route

javascript
abi.encodeWithSignature("route((bytes,bytes,uint256)[])", hops);

検証の経路を表す属性です。 宛先チェーンから送信元チェーンまでの検証パスを、タプルの配列で指定します。

各タプルには以下の3つの情報が含まれます。

フィールド 型 説明
gateway bytes 次のチェーンのステートを検証するために呼び出すアドレス。bytesエンコードされたアドレスで、他のゲートウェイを呼び出して次のホップにメッセージを転送することもできる
proof bytes その検証ステップで必要な暗号学的証明データ
version uint256 検証ロジックのバージョン。0を指定するとどのバージョンでも受け入れる

versionにゼロ以外の値が指定されている場合、ゲートウェイはルートアドレスが要求されたバージョンを正確にサポートしていなければメッセージを拒否します。

ルートは有効なパスを構成する必要があります。 各ステップは、チェーン間の直接的なステート検証やトランザクション検証が可能な関係を表している必要があります。 無効または不完全な証明データを含むメッセージはゲートウェイによって拒否されます。

inclusionProof

javascript
abi.encodeWithSignature("inclusionProof(bytes)", proofData);

送信元チェーンのファイナライズされたブロックやトランザクションにおいて、特定のメッセージが確かに存在することを証明する暗号学的証明です。

ファイナライズ

ブロックチェーンにおいて、ブロックが確定し取り消せない状態になることです。 ファイナライズされたブロックのステートに基づいて証明を作成することで、ブロックの再編成(Reorg)による証明の無効化を防ぎます。

EVMチェーンの場合、これはイベント包含証明(Event Inclusion Proof)やストレージ証明(Storage Proof)として実装されます。 イベント包含証明とは、特定のイベントがトランザクションレシート内に含まれていることをMerkle Proofで証明するものです。 ストレージ証明は、コントラクトの特定のストレージスロットに特定の値が保存されていることをMerkle Patricia Trieで証明します。

受信側のERC7786レシーバーは、送信元チェーンの証明システムに従ってこの証明を検証する必要があります。

targetBlock

javascript
abi.encodeWithSignature("targetBlock(uint256)", blockNumber);

メッセージがコミットされた送信元チェーンのブロック番号またはブロック高を指定する任意の属性です。 連続的なブロック番号を使用しないチェーンの場合は、同等のコミットメント識別子を表します。

受信側は、この属性を使ってメッセージの鮮度(古すぎないか)やファイナリティの要件をチェックできます。 ただし、ユースケースによってはこの属性が不要な場合もあるため、無視しても構いません。

この属性が提供される場合、暗号学的証明で証明される状態のブロックと一致している必要があります。

検証プロセス

メッセージの検証は以下のステップで行われます。

flowchart TD
    A[メッセージ受信] --> B[属性のパース]
    B --> C{必須属性が揃っているか}
    C -->|No| D[メッセージ拒否]
    C -->|Yes| E[ルート検証]
    E --> F{各ホップの証明が有効か}
    F -->|No| D
    F -->|Yes| G[inclusionProof検証]
    G --> H{メッセージが送信元のステートに存在するか}
    H -->|No| D
    H -->|Yes| I{targetBlockの検証}
    I -->|失敗| D
    I -->|成功/スキップ| J[メッセージ実行]

具体的な手順は以下の通りです。

  1. メッセージからrouteとinclusionProof属性をパースし、targetBlockが提供されていればそれも取得します。
  2. 必須属性がすべて存在し、形式が正しいことを確認します。
  3. ルートの各ステップについて、ブロックハッシュの遷移または同等のステートコミットメントを対応する証明を使って検証します。 バージョン要件がゼロ以外で指定されている場合はバージョンも検証します。
  4. inclusionProofを使って、メッセージデータが送信元チェーンのコミット済みステートにルート検証で得られた対象ブロック時点で存在することを検証します。 送信元チェーンは、ルート検証プロセスの最終ステップで検証されたチェーンに対応している必要があります。
  5. targetBlock属性が提供されていて、受信者がそれを検証する場合は、鮮度やファイナリティの要件を満たしているか確認します。
  6. すべての検証にパスすれば、メッセージを実行します。

マルチホップルーティング

直接の検証関係がないチェーン間でも、中間チェーンを経由することでメッセージの正当性を確認できます。 これがマルチホップルーティングです。

sequenceDiagram
    participant D as 宛先チェーン
    participant M as 中継チェーン
    participant S as 送信元チェーン

    Note over S: メッセージをコミット
    Note over D: 検証を開始(宛先→送信元の方向)
    D->>M: ホップ1で中継チェーンのステートを検証
    D->>S: ホップ2で送信元チェーンのステートを検証
    Note over D: 両方の証明が有効なら<br/>メッセージを実行

検証はルートに記された順序で宛先チェーンから送信元チェーンに向かって行われます。 ホップ1が宛先に最も近い中継チェーンの検証で、最後のホップが送信元チェーンのステートの検証に対応します。

以下の図は、マルチホップ検証の全体フローを示しています。

マルチホップ検証フロー図

例えば、送信元チェーンと宛先チェーンの間に直接のステート検証手段がない場合でも、両方のチェーンと検証関係を持つ中継チェーンが存在すれば、2つのホップを経由してメッセージを検証できます。

各ホップは「前のチェーンのステートを検証できる」という関係を表しています。 ルート全体で信頼の連鎖を構築し、最終的に送信元チェーンのステートの真正性を確立します。 この仕組みは、ERC7888のBlockHashProverチェーンと似た概念です。

ERC7888については以下を参考にしてください。

マルチホップのセキュリティ

マルチホップルートのセキュリティは、検証パスの中で最も弱い証明によって決まります。 複数のルートステップを使用する場合、全体のセキュリティレベルは、暗号学的保証が最も低いステップ、または合意メカニズムが最も安全性の低いステップによって決定されます。

つまり、3つのホップがあって2つが非常に安全でも、1つが弱ければ全体の安全性はその弱いホップと同じレベルになるということです。 ルートを設計する時は、各ホップの安全性を慎重に評価する必要があります。

キャッシングと不変性

キャッシング

暗号学的証明は決定論的(同じ入力に対して常に同じ結果を返す)であるため、検証結果を安全にキャッシュできます。 特にブロードキャストメッセージのように複数回の検証が必要なケースでは、キャッシュが大きな性能改善をもたらします。

ゲートウェイは、ブロックハッシュの遷移結果と証明の検証結果の両方をキャッシュし、証明インフラの変更時にはキャッシュを無効化する必要があります。

メッセージコミットメントの不変性

ゲートウェイは、メッセージを一度設定したら削除や変更ができない方法でコミット(記録)することを選択できます。 これは不変性の保証を提供します。

不変性がなぜ重要かというと、もしコミットされたメッセージが後から削除できる場合、削除後も証明自体は有効なままになるためです。 送信者が「このメッセージは送った」と証明できる一方で、送信元チェーン上ではメッセージが消えているという矛盾が生じます。

EVMチェーンの場合、ERC7201を使ったnamespace付きストレージスロットに不変データとして保存する方法が推奨されています。

補足

既存の証明プロトコルとの関係

ERC7965は、既存の証明プロトコルがERC7786ゲートウェイを実装するための標準的な属性を提供します。

例えば、ERC7888(EVMストレージ証明のプロトコル)のBroadcasterは、既存のストレージ証明アーキテクチャを維持しながらERC7786インターフェースを公開できます。 つまり、検証ロジックを一から作り直す必要はなく、ERC7965の属性を通じて既存の証明システムをそのまま活用できるということです。

他の証明システム(ゼロ知識証明ベースのものなど)も、同じ属性を使って独自の証明メカニズムを実装できます。

属性設計の判断理由

ERC7965の属性設計にはいくつかの意図的な判断があります。

route属性がタプルの配列で設計されているのは、ルート情報を単一のタプルにまとめることで型安全性を維持しつつ、証明の検証とチェーンステートの遷移を独立して最適化できるようにするためです。

targetBlockが任意属性になっているのは、実装の柔軟性を確保するためです。 基本的な検証にはrouteとinclusionProofの2つがあれば十分で、targetBlockによる鮮度チェックはすべてのユースケースに必要なわけではありません。 不変なメッセージコミットメントを提供するゲートウェイでは、メッセージが古くても証明が有効であればそれで十分な場合があります。

互換性

ERC7786との後方互換性

ERC7965は、ERC7786の属性システムを拡張する形で設計されており、既存の実装に対する変更はありません。 暗号学的証明属性をサポートしていないゲートウェイは、これらの属性を含むメッセージを単純に拒否します。 これはサポートされていない機能に対する期待される動作であり、既存システムの動作に影響を与えるものではありません。

既存のERC7786のツールやインフラは、基本インターフェースが変更されていないため、修正なしで暗号学的証明メッセージングを活用できます。

セキュリティ

バリデーション要件

ゲートウェイはメッセージの偽造を防ぐために、すべての証明データを厳密に検証する必要があります。 検証すべき項目は、証明の形式、完全性、暗号学的な妥当性です。

ルートアドレスは、チェーン間の有効で接続されたパスを構成する正当な証明インフラに対応している必要があります。 ファイナライズされたブロックまたは同等のコミットメントポイントのみを証明生成に使用する必要があります。 未ファイナライズのブロックを使うと、ブロックの再編成(Reorg)により証明が無効化されるリスクがあります。

暗号学的証明メッセージの利用者は、適切な鮮度チェックを実装する必要があります。 証明は任意の過去のブロックやコミットメント時点のメッセージを検証できるため、非常に古いメッセージの証明も有効になり得ます。 ただし、不変なメッセージコミットメントを提供するゲートウェイの場合は、この制限は必須ではありません。

ブロードキャストメッセージのセキュリティ

ブロードキャストメッセージは誰でも実行できるため、受信者はメッセージの送信元と内容を厳密に検証する必要があります。 送信者の権限とメッセージの意味的な妥当性の両方を確認することが重要です。

例えば、「緊急停止」というブロードキャストメッセージを受け取った場合、本当に権限を持つアドレスから送られたものかどうかを検証しなければ、悪意のある第三者が偽の停止命令を送ることで正規のサービスを止められてしまいます。

参考実装

Hop構造体

javascript
struct Hop {
    bytes gateway;
    bytes proof;
    uint256 version;
}

Hopは、マルチホップルーティングにおける1つの検証ステップを表す構造体です。 gatewayには次のチェーンのステートを検証するために呼び出すゲートウェイアドレスが入ります。 このアドレスは他のゲートウェイを呼び出して次のホップにメッセージを転送することもできます。 proofにはそのホップの検証で使う暗号学的証明データが、versionには検証ロジックのバージョン要件が入ります。 versionを0にすると、バージョンの制限なしに検証を受け入れます。

ルートの準備

javascript
// ルートの準備(証明とバージョン要件を含む)
Hop[] memory hops = new Hop[](2);
hops[0] = Hop(gateway1, proof1, 1); // バージョン1を要求
hops[1] = Hop(gateway2, proof2, 0); // バージョン制限なし

2ホップのルートを作成する例です。 最初のホップはバージョン1の検証ロジックを要求し、2番目のホップはどのバージョンでも受け入れます。

ブロードキャストパターンの実装例

以下は3つのブロードキャストパターンのERC7930アドレスの構築方法です。

アドレスブロードキャスト
javascript
// Arbitrum One(42161)上の全アドレスに送信
bytes memory addressBroadcast = abi.encodePacked(
    uint16(1),        // Version
    uint16(0x0000),   // ChainType: eip155(EVMチェーン)
    uint8(2),         // ChainReferenceLength: 2バイト
    uint16(42161),    // ChainReference: Arbitrum One
    uint8(0)          // AddressLength: 0(全アドレスへブロードキャスト)
);

ChainTypeとChainReferenceで特定のチェーンを指定し、AddressLengthを0にすることでそのチェーン上のすべてのアドレスにブロードキャストします。

チェーンタイプブロードキャスト
javascript
// すべてのEIP-155チェーン(EVM互換チェーン全体)に送信
bytes memory chainTypeBroadcast = abi.encodePacked(
    uint16(1),        // Version
    uint16(0x0000),   // ChainType: eip155
    uint8(0),         // ChainReferenceLength: 0(全チェーンへブロードキャスト)
    uint8(0)          // AddressLength: 0(全アドレス)
);

ChainTypeを指定し、ChainReferenceLengthを0にすることで同じタイプのチェーンすべてに送信します。

ユニバーサルブロードキャスト
javascript
// 対応するすべてのチェーンとアドレスに送信
bytes memory universalBroadcast = abi.encodePacked(
    uint16(1),        // Version
    uint16(0x0000),   // ChainType: 0(全チェーンタイプ)
    uint8(0),         // ChainReferenceLength: 0(全チェーン)
    uint8(0)          // AddressLength: 0(全アドレス)
);

すべてのフィールドをゼロにすることで、対応するすべてのインフラにメッセージを配信します。

メッセージ送信

javascript
bytes[] memory attributes = new bytes[](3);
attributes[0] = abi.encodeWithSignature("route((bytes,bytes,uint256)[])", hops);
attributes[1] = abi.encodeWithSignature("inclusionProof(bytes)", proofData);
attributes[2] = abi.encodeWithSignature("targetBlock(uint256)", blockNumber);

// アドレスブロードキャストでメッセージ送信
gateway.sendMessage(
    addressBroadcast,
    abi.encode("priceUpdate", asset, price),
    attributes
);

// チェーンタイプブロードキャストでメッセージ送信
gateway.sendMessage(
    chainTypeBroadcast,
    abi.encode("governanceProposal", proposalId, votingPeriod),
    attributes
);

// ユニバーサルブロードキャストでメッセージ送信
gateway.sendMessage(
    universalBroadcast,
    abi.encode("pause", reason),
    attributes
);

属性を配列にまとめてsendMessageに渡します。 payloadにはメッセージの本体データ(価格更新、ガバナンス提案、緊急停止など)が入り、attributesには証明メタデータが入ります。

引用

最後に

今回は「ERC7965による暗号学的証明を使ったクロスチェーンメッセージの検証とブロードキャスト」についてまとめてきました。 いかがだったでしょうか?

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

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