はじめに

今回は「ブロックチェーンのBridge」について改めて考え直してみようと思います。

以下の記事もBridgeについてまとめているのでよければ参考にしてください。

Bridgeとは

Bridgeとはの図

ブロックチェーンのBridgeとは、異なるブロックチェーン同士の間で資産(トークン)やデータを移動・共有できるようにする仕組みのことです。 Bitcoin、Ethereum、Solana、Base、Avalancheのようなブロックチェーンは、それぞれ独立したネットワークであり、本来は互換性がありません。 そこでブリッジが資産(トークン)やデータの「橋渡し」の役割を担い、チェーン間の流動性やアプリケーション連携を可能にします。

Bridgeは大きく分けて2つのパターンがあります。

メッセージの送付

メッセージの送付の図

1つ目は、シンプルに文字列を他のブロックチェーンに送付するパターンです。 例えば、Avalancheチェーン上のスマートコントラクトから、Baseチェーン上のスマートコントラクトに Hello などの文字列を送付する仕組みです。 これは単に文字列を送付するだけのため、送付先のブロックチェーン上のスマートコントラクトで、受け取った文字列を元に処理を実行するイメージです。

実行情報の送付

実行情報の送付の図

2つ目は、スマートコントラクト内の関数実行情報を送付するパターンです。 例えば、Avalancheチェーン上のスマートコントラクトから、Baseチェーン上のスマートコントラクト内に実装されている関数の実行情報を渡します。

実行情報の送付の図

Solidityでは、上図のように関数シグネチャと引数をABIエンコードすることができます。 この関数の実行情報を以下のように実行することもできます。

text
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Caller {
    function callMint(address target, address to, uint256 value) external returns (bool) {
        // 関数シグネチャ + 引数を ABI エンコードして calldata を作成
        bytes memory data = abi.encodeWithSignature("mint(address,uint256)", to, value);

        // low-level call で外部コントラクトを呼び出す
        (bool success, ) = target.call(data);
        require(success, "call to target contract failed");

        return success;
    }
}

他のブロックチェーンに送付する場合は、その最終処理をする関数(ここでは sendCrossChainMessage())を呼び出します。

text
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Caller {
    function callMint(address target, address to, uint256 value) external returns (bool) {
        // 関数シグネチャ + 引数を ABI エンコードして calldata を作成
        bytes memory data = abi.encodeWithSignature("mint(address,uint256)", to, value);

        // 他のブロックチェーン上に関数実行情報を送付
        sendCrossChainMessage(data);
    }
}

詳細は以下の記事などが参考になります。 https://www.saxenism.com/blog/solidity-encodings https://rareskills.io/post/abi-encoding

2つのパターンと分けていますが、実際はどちらも「メッセージを送付する」処理です。 関数実行についても、関数実行のメッセージを送付しているだけなので。

Bridgeのフロー

先ほどBridgeのパターンを紹介しました。 この章では、実際にBridgeを行うときのフローを具体例とともに紹介していきます。 これでBridgeについてさらに理解を深められるはずです。

メッセージの送付を待つ

メッセージの送付を待つの図

  1. メッセージを送付して mint 関数を実行
  2. mint 関数の実行成功を呼び出し元のコントラクトの receive 関数を実行して通知

Avalanche上のコントラクトから、Base上のコントラクト内の mint 関数を呼び出すメッセージを送付します。 mint 関数の最後に、呼び出し元であるAvalanche上のコントラクトにメッセージを送り返す処理を追加しておきます。 mint 関数の処理が完了した時のみ、呼び出し元であるAvalanche上のコントラクトに完了のメッセージが送付され、Avalanche上のコントラクトでMintが成功したことを把握することができます。

  • 送付元のコントラクトの例
text
contract NFT {
    function mint(address to, uint256 tokenId) public {
        _mint(to, tokenId);

        // Request 側の onMessage(bytes) で受け取る想定
        bytes memory data = abi.encodeWithSignature(
            "onMessage(bytes)",
            abi.encode(
                "Mint Success!",
                to,
                tokenId
            )
        );

        Bridge.sendMessage(data);
    }
}
  • 送付先のコントラクトの例
text
contract Request {
    function request(address to, uint256 tokenId) public {
        bytes memory data = abi.encodeWithSignature(
            "mint(address,uint256)",
            to,
            tokenId
        );

        Bridge.sendMessage(data);
    }

    // Bridge から bytes をそのまま受け取る
    function onMessage(bytes calldata message) public {
        (
            string memory result,
            address to,
            uint256 tokenId
        ) = abi.decode(
            message,
            (string, address, uint256)
        );

        // ここで result / to / tokenId を使った処理を書く
    }
}

このように送付されたメッセージが実行されたかを、リクエスト元のチェーン上のコントラクトで知ることができ、記録しておくことができるようになります。

Burn & Mint

Burn & Mintの図

こちらも別記事になってしまうのですが、以下の記事で詳しく解説しています。

簡単にまとめると、送付元チェーン上で送付したいトークンをBurnして、送付先チェーン上で新たにトークンを発行しています。 これにより、複数のブロックチェーンでトークンの総供給量を一定に保つことができます。

Lock & Mint

Lock & Mintの図

別記事になってしまうのですが、以下の記事で詳しく解説しています。

簡単にまとめると、送付元チェーン上で送付したいトークンをLockしてコントラクト内で保持しておき、送付先チェーン上で新たにトークンを発行しています。 ブロックチェーンごとにトークンの総供給量を常に一定に保つことができます。

Relayer

ここまではわかりやすくするために取り上げてこなかったのですが、この後の章で説明するBridgeサービスではオフチェーンでメッセージの中継や検証を行います。 特にメッセージの中継を行う主体をRelayerと呼びます。

Relayerの図

上記のように2つのブロックチェーンの間に立ち、送信元チェーンから発行されたリクエスト情報を取得しにいき、送信先チェーンにメッセージを送ります(この時トランザクションを発行します)。 通常、このリクエスト情報はイベントなどで発行され、Relayerがそのイベントを監視する構成などが取られています。

Relayerの図

また、通常、コントラクトからBridgeコントラクトを呼んでもらう構成がほとんどです。 RelayerはこのBridgeコントラクトから発行されるイベントを監視します。 そして、送信先ブロックチェーン上のBridgeコントラクト内で検証を行うようなイメージです。

Relayerの図

この時Relayerは、リクエストが正規のものかを検証をValidatorなど検証を行う主体に渡します。 ここで検証結果が正しいという署名なり情報が送信先チェーン上のBridgeコントラクトに渡されて、そこでチェックが行われ、最終的に送信先コントラクトにメッセージが渡されます。

おおまかに説明するとこのような構成になっています。

Bridgeサービスの紹介

この章では、主要なBridgeサービスについて簡単に紹介していきます。

この章は主に以下を使用して公式ドキュメントなどをもとにまとめて、筆者が内容を修正する形をとっています。

Layer Zero

まずはLayer Zeroです。 LayerZero は、複数のブロックチェーン間でスマートコントラクト同士が任意のデータ・関数呼び出し等のメッセージを送受信できるようにするプロトコルです。

LayerZeroの5層アーキテクチャ

LayerZero は、役割ごとに分けた5つの層から構成されています。 それぞれの層は独立しており、柔軟に組み合わせることができます。

レイヤー 名称 役割
Layer 1 OApp(Business Logic Interface) アプリケーション側のメッセージ送受信窓口
Layer 2 Endpoint(Protocol Interface) クロスチェーンメッセージの出入口
Layer 3 Message Libraries メッセージ処理や検証のルール定義
Layer 4 DVN(Decentralized Verifier Networks) メッセージの正しさを検証するサービス
Layer 5 Executors 検証済みメッセージを届けるサービス

アーキテクチャの全体像

LayerZeroの5層構造を図で表すと以下のようになります。

graph TB
    subgraph "Source Chain"
        OApp1[OApp<br/>Business Logic]
        Endpoint1[Endpoint<br/>Protocol Interface]
        MessageLib1[Message Library<br/>Verification Rules]
    end

    subgraph "Verification Layer"
        DVN1[DVN 1]
        DVN2[DVN 2]
        DVN3[DVN N]
    end

    subgraph "Execution Layer"
        Executor[Executor<br/>Message Delivery]
    end

    subgraph "Destination Chain"
        MessageLib2[Message Library<br/>Verification Rules]
        Endpoint2[Endpoint<br/>Protocol Interface]
        OApp2[OApp<br/>Business Logic]
    end

    OApp1 -->|_lzSend| Endpoint1
    Endpoint1 -->|Generate Packet| MessageLib1
    MessageLib1 -.->|Request Verification| DVN1
    MessageLib1 -.->|Request Verification| DVN2
    MessageLib1 -.->|Request Verification| DVN3
    DVN1 -.->|Verify| MessageLib2
    DVN2 -.->|Verify| MessageLib2
    DVN3 -.->|Verify| MessageLib2
    Executor -->|Deliver Verified Message| Endpoint2
    Endpoint2 -->|_lzReceive| OApp2

各レイヤーは独立しており、開発者は用途に応じて柔軟に設定を変更できます。

Layer 1: OApp(Business Logic Interface)

OAppは、開発者側のスマートコントラクトAPI層です。

Omnichain Applications (OApps) are LayerZero's definition for smart contracts that use LayerZero to send and receive cross-chain messages."

アプリケーションの処理内容(例: トークン発行、NFTのmint、特定関数呼び出しなど)を、LayerZeroで送信/受信できる形に変換する役割を持ちます。

主な機能

  • _lzSend() LayerZero経由でメッセージ送信を行います。
  • _lzReceive() メッセージを受信したときに実行される処理を行います。

OAppを使用することで、開発者はプロトコルの複雑な仕組みを気にせず、アプリケーションの処理内容やステートの変化に集中できます。

Layer 2: Endpoint(Protocol Interface)

Endpointは、すべてのクロスチェーンメッセージングにおける「出入口」として機能します。 メッセージの送信時はここから出て、受信時はここから入ってきます。

Endpointの特徴

  • Universal interface(ユニバーサルインターフェース) すべてのサポートされているチェーンで共通のインターフェースを提供します。
  • Immutable(不変性) 将来のアップグレードを防ぐ不変設計になっています。
  • Permissionless(許可不要) 中央管理者による許可が不要で、誰でも自由にアクセス可能です。
  • Chain-agnostic(チェーン非依存) EVM、Solana、Aptosなど、あらゆるチェーンで動作します。

各Endpointは、メッセージの順序を管理する番号(nonce)を保存し、すべてのメッセージに一意な識別番号(GUID)を付与します。 これにより、メッセージが正しい順序で処理されることを保証します。

Layer 3: Message Libraries

Message Librariesは、メッセージの処理方法を定義するルールの集まりです。 メッセージをどのように検証し、どのように送信するかといった一連の流れをルールとして定めています。 ライブラリは通信経路(チャネル)ごとに異なる設定を行うことができます。

設定オプション

  • Finality requirements(最終性要件) メッセージを確定とみなすために必要なブロック数を指定します。
  • Verification rules(検証ルール) どのDVN(検証者)が検証を行う必要があるかを定義します。
  • Execution parameters(実行パラメータ) メッセージを届けるサービスの設定を行います。

X-of-Y-of-N検証パターン

ライブラリは、X-of-Y-of-Nパターンで検証要件を調整します。

  • X 必要な検証者の数を示します。
  • Y 必要な検証の総数を示します。
  • N 利用可能なDVNのプールを示します。

このパターンにより、セキュリティと柔軟性のバランスを取ることができます。

Layer 4: DVN(Decentralized Verifier Networks)

DVNは、メッセージの正しさを検証する独立したサービスです。 それぞれのDVNは異なる方法でメッセージを検証し、信頼性を確保します。

検証方法の例

  • ZK proofs(ゼロ知識証明) ゼロ知識証明を使用した検証を行います。
  • Consensus committees(コンセンサス委員会) コンセンサス委員会による検証を行います。
  • Light clients(ライトクライアント) ライトクライアントを使用した検証を行います。

設定は通信経路ごとに柔軟に変更でき、多様なセキュリティ戦略を実現できます。 例えば、高セキュリティが求められる経路では複数のDVNを要求し、低リスクの経路では単一のDVNで済ませることができます。

Layer 5: Executors

Executorsは、検証済みのメッセージを宛先チェーンに届けるサービスです。 誰でも許可なくExecutorを運用できるため、複数のExecutorが競争してメッセージを届けます。 これにより、特定の事業者によるメッセージの検閲(意図的な阻止)を防ぐことができます。

Executorは、DVNによって検証されたメッセージを受け取り、宛先チェーンのOAppの _lzReceive() で処理できる形で届けます。

メッセージフロー全体

LayerZeroのメッセージフローは、大きく「送信側のプロセス」と「受信側のプロセス」に分けられます。

送信側のプロセス(メッセージ送信と検証リクエスト)

送信チェーン上でメッセージを作成し、DVNsに検証を依頼するまでの流れです。

sequenceDiagram
    participant OApp1 as OApp<br/>(送信元)
    participant Endpoint1 as Endpoint<br/>(送信元)
    participant MessageLib1 as Message Library<br/>(送信元)
    participant DVN1 as DVN 1
    participant DVN2 as DVN 2
    participant DVNN as DVN N

    Note over OApp1,DVNN: 送信側プロセス開始

    OApp1->>Endpoint1: 1. _lzSend(message)
    Note right of OApp1: アプリケーションから<br/>メッセージ送信

    Endpoint1->>Endpoint1: 2. パケット生成<br/>(nonce, GUID)
    Note right of Endpoint1: 順序番号と<br/>識別番号を付与

    Endpoint1->>MessageLib1: 3. パケット処理
    Note right of MessageLib1: 設定された<br/>検証ルールを適用

    par DVNsへ検証リクエスト
        MessageLib1->>DVN1: 4a. 検証リクエスト
        MessageLib1->>DVN2: 4b. 検証リクエスト
        MessageLib1->>DVNN: 4c. 検証リクエスト
    end

    Note over DVN1,DVNN: 各DVNが独立して<br/>メッセージの正しさを検証

    Note over OApp1,DVNN: 送信側プロセス完了<br/>検証結果を待機

受信側のプロセス(検証完了とメッセージ配送)

DVNsの検証が完了し、メッセージが受信チェーンに届くまでの流れです。

sequenceDiagram
    participant DVN1 as DVN 1
    participant DVN2 as DVN 2
    participant DVNN as DVN N
    participant MessageLib2 as Message Library<br/>(受信先)
    participant Executor as Executor
    participant Endpoint2 as Endpoint<br/>(受信先)
    participant OApp2 as OApp<br/>(受信先)

    Note over DVN1,OApp2: 受信側プロセス開始

    par 検証結果を受信先に送信
        DVN1-->>MessageLib2: 5a. 検証完了(証明)
        DVN2-->>MessageLib2: 5b. 検証完了(証明)
        DVNN-->>MessageLib2: 5c. 検証完了(証明)
    end

    MessageLib2->>MessageLib2: 6. 検証数チェック<br/>(X-of-Y-of-N)
    Note right of MessageLib2: 必要な検証数が<br/>揃ったか確認

    MessageLib2->>Executor: 7. メッセージ配送準備完了
    Note right of Executor: 検証済みメッセージを<br/>受け取り

    Executor->>Endpoint2: 8. 検証済みメッセージ配送
    Note right of Executor: 受信先チェーンに<br/>メッセージを届ける

    Endpoint2->>OApp2: 9. _lzReceive(message)
    Note right of OApp2: アプリケーションで<br/>メッセージ処理

    Note over DVN1,OApp2: 受信側プロセス完了<br/>クロスチェーンメッセージング完了

フローのまとめ

  1. 送信元チェーンでの処理 送信元のOAppが _lzSend() を呼び出し、Endpointがメッセージに順序番号と識別番号を付けます。 Message Libraryが設定されたルールに従ってパケットを処理し、複数のDVNsに検証をリクエストします。
  2. DVNsによる検証 各DVNが独立してメッセージの正しさを検証し、検証結果(証明)を受信先のMessage Libraryに送信します。
  3. 受信先チェーンでの処理 受信先のMessage Libraryが必要な検証数が揃ったか確認し、Executorがメッセージを受信先チェーンに届けます。 最後に受信先のOAppの _lzReceive() でメッセージが処理されます。

X-of-Y-of-N検証パターンの詳細

X-of-Y-of-N検証パターンは、LayerZeroのセキュリティの中核を担う仕組みです。

パターンの意味

  • N(プール) 利用可能なDVNの総数を示します。
  • Y(必要な検証数) メッセージが有効とみなされるために必要な検証の総数を示します。
  • X(必須の検証者) 必ず検証を行う必要があるDVNの数を示します。

例

プールに5つのDVN(N=5)があり、そのうち3つの検証が必要(Y=3)で、特定の2つのDVNが必ず検証を行う必要がある(X=2)場合、以下のようになります。

graph LR
    subgraph "DVN Pool (N=5)"
        DVN1[DVN 1<br/>Required]
        DVN2[DVN 2<br/>Required]
        DVN3[DVN 3<br/>Optional]
        DVN4[DVN 4<br/>Optional]
        DVN5[DVN 5<br/>Optional]
    end

    subgraph "Verification (Y=3)"
        V1[Verification 1]
        V2[Verification 2]
        V3[Verification 3]
    end

    DVN1 -.->|Must Verify| V1
    DVN2 -.->|Must Verify| V2
    DVN3 -.->|Optional| V3
    DVN4 -.->|Optional| V3
    DVN5 -.->|Optional| V3

    style DVN1 fill:#f96,stroke:#333
    style DVN2 fill:#f96,stroke:#333
    style DVN3 fill:#9cf,stroke:#333
    style DVN4 fill:#9cf,stroke:#333
    style DVN5 fill:#9cf,stroke:#333

この設定では、DVN 1とDVN 2は必ず検証を行い、残りの3つのDVNのうち1つが追加で検証を行う必要があります。 これにより、信頼性の高いDVNを必須とし、追加の検証を柔軟に設定できます。

チャネルアーキテクチャ

チャネルは、4つのコンポーネントによって一意に識別されます。

  • Sender OApp 送信元のOAppを指します。
  • Source Endpoint 送信元のEndpointを指します。
  • Destination Endpoint 宛先のEndpointを指します。
  • Receiver OApp 受信先のOAppを指します。

各チャネルは独立した順序管理番号(nonce)と個別のライブラリ設定を持っています。 これにより、異なる経路間でメッセージを同時に送受信しても、メッセージの順序が混ざることを防ぐことができます。

次にChainlink CCIP(Cross-Chain Interoperability Protocol)について説明していきます。 Chainlink CCIPは、トークン転送、メッセージング、プログラマブルトークン転送のための安全なクロスチェーン相互運用プロトコルです。

概要

Chainlink CCIPは、異なるブロックチェーン間でアプリケーションが通信し、資産を転送できるようにするプロトコルです。

主な機能

  • Arbitrary Messaging(任意のメッセージング) スマートコントラクトがチェーン間でカスタムデータや命令を送信できます。 複雑なクロスチェーンロジックや分散アプリケーション間の通信を実現します。
  • Token Transfer(トークン転送) 異なるブロックチェーンネットワーク間でトークンを直接移動できます。 エコシステム全体で標準化された資産転送をサポートします。
  • Programmable Token Transfer(プログラマブルトークン転送) メッセージングとトークン転送を組み合わせた高度な機能です。 トークンを実行可能な命令とともに送信し、洗練されたクロスチェーンやり取りを可能にします。

CCIPの2層アーキテクチャ

Chainlink CCIPは、「オンチェーン層」と「オフチェーン層」の2つの層で構成されています。

層 名称 役割
オンチェーン層 Router, OnRamp, OffRamp メッセージ送受信、手数料見積もり、トークン処理
オフチェーン層 Committing DON, Executing DON, RMN メッセージ検証、実行、セキュリティ監視

アーキテクチャの全体像

Chainlink CCIPの2層構造を図で表すと以下のようになります。

graph TB
    subgraph "Source Chain"
        User1[User/dApp]
        Router1[Router Contract]
        OnRamp[OnRamp Contract]
        TokenPool1[Token Pool]
    end

    subgraph "Offchain Layer"
        CommitDON[Committing DON<br/>合意形成]
        RMN[Risk Management Network<br/>リスク管理]
        ExecDON[Executing DON<br/>実行]
    end

    subgraph "Destination Chain"
        OffRamp[OffRamp Contract]
        Router2[Router Contract]
        TokenPool2[Token Pool]
        Receiver[Receiver Contract]
    end

    User1 -->|1. メッセージ送信| Router1
    Router1 -->|2. 処理| OnRamp
    OnRamp -->|3. トークンロック/バーン| TokenPool1
    OnRamp -.->|4. イベント検知| CommitDON
    CommitDON -.->|5. 検証| RMN
    CommitDON -->|6. Merkle Root| OffRamp
    ExecDON -.->|7. メッセージ実行| OffRamp
    OffRamp -->|8. トークン解放/ミント| TokenPool2
    OffRamp -->|9. メッセージ配送| Router2
    Router2 -->|10. 受信処理| Receiver

各層は独立しており、セキュリティと柔軟性を両立しています。

オンチェーン層:主要コンポーネント

オンチェーン層は、各ブロックチェーン上にデプロイされたスマートコントラクトで構成されています。

Router Contract

各ブロックチェーンには単一の不変なRouterコントラクトがあり、ユーザーと分散アプリケーション(dApps)の統一インターフェースとして機能します。 Routerは、メッセージの送受信、手数料の見積もり、適切なOnRamp/OffRampへのルーティングを担当します。

OnRamp Contract

送信元チェーン上でメッセージの出口として機能します。 OnRampは以下の処理を行います。

  • メッセージの検証 送信されるメッセージの形式とパラメータを確認します。
  • 手数料の徴収 クロスチェーンメッセージ送信に必要な手数料を徴収します。
  • トークンの処理 Token Poolと連携して、トークンのロックまたはバーン処理を行います。
  • イベントの発行 オフチェーン層が検知できるようにイベントを発行します。

OffRamp Contract

受信先チェーン上でメッセージの入口として機能します。 OffRampは以下の処理を行います。

  • レポートの検証 Committing DONから受け取った合意レポートを検証します。
  • トークンの解放/ミント Token Poolと連携して、トークンを受信者に届けます。
  • メッセージの配送 検証済みメッセージをRouterを通じて受信者に届けます。

Token Pool

トークン転送の具体的なメカニズムを実装します。 複数の転送モデルをサポートします。

  • Burn & Mint 送信元チェーンでトークンをBurnし、受信先チェーンでミント(新規発行)します。
  • Lock & Mint 送信元チェーンでトークンをロック(保管)し、受信先チェーンでミントします。
  • Lock & Release 送信元チェーンでトークンをロックし、受信先チェーンで既存のトークンを解放します。

オフチェーン層:Decentralized Oracle Networks(DON)

オフチェーン層は、分散型オラクルネットワーク(DON)で構成され、メッセージの検証と実行を担当します。

Committing DON(合意形成ネットワーク)

送信元チェーンのイベントを監視し、合意ベースのレポートを作成します。

  • イベントの監視 送信元チェーンのOnRampコントラクトが発行するイベントを監視します。
  • 検証と合意 複数のノードが独立してメッセージを検証し、合意を形成します。
  • Merkle Rootの生成 検証済みメッセージのバッチからMerkle Rootを生成し、受信先チェーンに送信します。

Executing DON(実行ネットワーク)

検証済みメッセージを最適化し、受信先チェーンでの実行を担当します。

  • メッセージの検証 Committing DONが作成したレポートを再検証します。
  • 実行の最適化 ガス効率を考慮して、メッセージの実行順序を最適化します。
  • OffRampへの送信 検証済みメッセージを受信先チェーンのOffRampに送信します。

Risk Management Network(RMN)

追加のセキュリティ層として、重要な操作を監視します。

  • 異常の検知 通常とは異なるパターンや潜在的な脅威を検知します。
  • 緊急停止 必要に応じてシステムを一時停止し、資産を保護します。
  • 追加検証 Committing DONとは独立して、メッセージの正当性を検証します。

メッセージフロー全体

Chainlink CCIPのメッセージフローは、「送信側のプロセス」と「受信側のプロセス」に分けられます。

送信側のプロセス(メッセージ送信と合意形成)

送信チェーン上でメッセージを作成し、Committing DONが合意を形成するまでの流れです。

sequenceDiagram
    participant User as User/dApp
    participant Router1 as Router<br/>(送信元)
    participant OnRamp as OnRamp
    participant Pool1 as Token Pool<br/>(送信元)
    participant CommitDON as Committing DON
    participant RMN as Risk Management<br/>Network

    Note over User,RMN: 送信側プロセス開始

    User->>Router1: 1. ccipSend(message)
    Note right of User: メッセージとトークンを<br/>送信リクエスト

    Router1->>Router1: 2. 手数料見積もり
    Note right of Router1: 必要な手数料を計算

    Router1->>OnRamp: 3. メッセージ転送
    Note right of OnRamp: OnRampに<br/>メッセージを渡す

    OnRamp->>Pool1: 4. トークン処理
    Note right of Pool1: Lock/Burn処理

    OnRamp->>OnRamp: 5. イベント発行
    Note right of OnRamp: CCIPSendRequestedイベント

    Note over CommitDON: イベントを監視・検知

    CommitDON->>CommitDON: 6. 合意形成
    Note right of CommitDON: 複数ノードで<br/>メッセージを検証

    CommitDON->>RMN: 7. セキュリティチェック
    Note right of RMN: 追加の<br/>セキュリティ検証

    CommitDON->>CommitDON: 8. Merkle Root生成
    Note right of CommitDON: メッセージバッチの<br/>Merkle Rootを作成

    Note over User,RMN: 送信側プロセス完了<br/>受信側での処理を待機

受信側のプロセス(実行と配送)

Executing DONがメッセージを実行し、受信者に届けるまでの流れです。

sequenceDiagram
    participant CommitDON as Committing DON
    participant ExecDON as Executing DON
    participant OffRamp as OffRamp
    participant Pool2 as Token Pool<br/>(受信先)
    participant Router2 as Router<br/>(受信先)
    participant Receiver as Receiver<br/>Contract

    Note over CommitDON,Receiver: 受信側プロセス開始

    CommitDON->>OffRamp: 1. Merkle Root送信
    Note right of OffRamp: 合意レポートを<br/>受信先チェーンに送信

    OffRamp->>OffRamp: 2. レポート検証
    Note right of OffRamp: Merkle Rootと<br/>署名を検証

    ExecDON->>ExecDON: 3. メッセージ取得
    Note right of ExecDON: 実行待ちメッセージを<br/>取得・最適化

    ExecDON->>OffRamp: 4. メッセージ実行リクエスト
    Note right of OffRamp: 検証済みメッセージの<br/>実行を依頼

    OffRamp->>Pool2: 5. トークン解放/ミント
    Note right of Pool2: Release/Mint処理

    OffRamp->>Router2: 6. メッセージ配送
    Note right of Router2: 検証済みメッセージを<br/>Routerに渡す

    Router2->>Receiver: 7. ccipReceive(message)
    Note right of Receiver: 受信者コントラクトで<br/>メッセージ処理

    Note over CommitDON,Receiver: 受信側プロセス完了<br/>クロスチェーンメッセージング完了

フローのまとめ

  1. 送信元チェーンでの処理 ユーザーがRouterの ccipSend() を呼び出し、OnRampがメッセージとトークンを処理します。 OnRampがイベントを発行し、Committing DONが複数ノードで合意を形成します。 RMNが追加のセキュリティチェックを行い、Merkle Rootを生成します。
  2. オフチェーン層での処理 Committing DONが合意レポートを受信先チェーンに送信します。 Executing DONがメッセージを取得し、実行順序を最適化します。
  3. 受信先チェーンでの処理 OffRampがMerkle Rootを検証し、Token Poolがトークンを解放/ミントします。 Routerを通じて受信者コントラクトの ccipReceive() でメッセージが処理されます。

Lane(レーン)とは

CCIPでは、送信元チェーンと受信先チェーンのペアを「Lane(レーン)」と呼びます。 各Laneは独立した設定を持ち、異なるセキュリティパラメータやレート制限を適用できます。

Laneの特徴

  • 独立した設定 各Laneごとに異なるDON、RMN、Token Poolを設定できます。
  • 方向性 Chain A → Chain B と Chain B → Chain A は別々のLaneとして扱われます。
  • 柔軟性 重要度の高いLaneには追加のセキュリティ層を設定できます。

セキュリティ機能

Chainlink CCIPは、多層的なセキュリティアプローチ「Defense-in-Depth」を採用しています。

Rate Limiter(レート制限)

不正利用や攻撃を防ぐため、メッセージやトークン転送に制限を設けます。 短期間に大量の転送が発生した場合、自動的に停止します。

Manual Execution(手動実行)

例外的な状況では、メッセージを手動で実行できます。 これにより、システムの柔軟性と回復力が向上します。

Upgradability(アップグレード可能性)

プロトコルの改善やセキュリティパッチを適用できる仕組みがあります。 ガバナンスプロセスを通じて、安全にシステムを進化させることができます。

トークン転送メカニズムの詳細

CCIPは、複数のトークン転送モデルをサポートしています。

  • Burn & Mint

送信元チェーンでトークンをBurnし、受信先チェーンで新規発行します。 総供給量は常に一定に保たれます。

  • Lock & Mint

送信元チェーンでトークンをロックし、受信先チェーンで新規発行します。 元のトークンは保管され、いつでも引き出せます。

  • Lock & Release

送信元チェーンでトークンをロックし、受信先チェーンで既存のトークンを解放します。 両チェーンに流動性プールが必要です。

  • Token Pool の役割

Token Poolは、これらの転送メカニズムを実装するスマートコントラクトです。 各トークンは、適切なToken Poolに関連付けられており、転送時に自動的に呼び出されます。

Axelar

次にAxelarについて説明していきます。 Axelarは、異なるブロックチェーン間でトークン転送と通信を安全に実行できる相互運用性プラットフォームです。

Axelarの3層アーキテクチャ

Axelarは、Proof-of-Stake(PoS)で動作する独立したブロックチェーンを中心に構成されています。 各層が明確に分離されており、許可不要なクロスチェーンアプリケーションを実現します。

層 名称 役割
メッセージング層 Gateway Contracts メッセージ送受信、トークン処理の出入口
検証層 Validators, Verifier Contract Proof-of-Stake合意形成、クロスチェーン検証
配送層 Relayers メッセージ配送、トランザクションデータ転送

アーキテクチャの全体像

Axelarのアーキテクチャを図で表すと以下のようになります。

graph TB
    subgraph "Source Chain"
        User1[User/dApp]
        Gateway1[Gateway Contract]
        GasService[Gas Service]
    end

    subgraph "Axelar Network"
        Validators[Validators<br/>PoS Consensus]
        Verifier[Verifier Contract]
        Router[Message Router]
    end

    subgraph "Relayer Service"
        Relayer[Relayer]
    end

    subgraph "Destination Chain"
        Gateway2[Gateway Contract]
        Receiver[Receiver Contract]
    end

    User1 -->|1. callContract/callContractWithToken| Gateway1
    User1 -->|2. ガス代支払い| GasService
    Gateway1 -.->|3. イベント検知| Relayer
    Relayer -->|4. トランザクションデータ送信| Verifier
    Verifier -->|5. 検証リクエスト| Validators
    Validators -->|6. 投票・合意| Verifier
    Verifier -->|7. 承認データ| Router
    Router -.->|8. 承認済みメッセージ| Relayer
    Relayer -->|9. バリデーター署名送信| Gateway2
    Gateway2 -->|10. _execute 実行| Receiver

各コンポーネントは独立しており、セキュリティと柔軟性を両立しています。

メッセージング層:Gateway Contracts

Gateway Contractsは、各ブロックチェーンにデプロイされるAxelarの基盤コントラクトです。 送信元チェーンではメッセージを発行し、受信先チェーンではバリデーターの承認を検証してメッセージを実行します。

Gatewayの役割

  • 送信元チェーンでの処理

アプリケーションが callContract() または callContractWithToken() を呼び出すと、Gatewayはイベントを発行します。 このイベントをRelayersが検知し、Axelarネットワークへのメッセージング プロセスが開始されます。

  • 受信先チェーンでの処理

Relayerがバリデーターの承認データを送信すると、Gateway はバリデーターの署名を検証します。 検証に成功すれば、受信先アプリケーションの _execute() 関数を呼び出してメッセージを配送します。

  • セキュリティモデル

Gatewayのセキュリティは、閾値暗号(threshold cryptography)とバリデーターの投票に依存しています。 トークンのミントやメッセージ転送など重要な操作には、バリデーターの投票による承認が必要です。

検証層:Axelar Network と Validators

Axelar Networkは、Proof-of-Stake(PoS)で動作する独立したブロックチェーンです。 Validatorsは、Axelar Network上でブロック生成と外部チェーンの監視の2つの役割を担います。

Validatorsの2つの役割

  • Axelar Networkの合意形成

標準的なPoSコンセンサスに参加し、Axelar Network自体のブロック生成とトランザクション検証を行います。 他のPoSチェーンと同様に、ネットワークの整合性を維持します。

  • クロスチェーントランザクションの検証

外部チェーン(Ethereum、Avalancheなど)のアクティビティを監視します。 送信元チェーンでGatewayがイベントを発行すると、Validatorsはそのトランザクションの正当性を確認して投票します。

Verifier Contractによる検証プロセス

Axelar Network上のVerifier Contractが、送信元チェーンのトランザクション検証を管理します。 主なモデルは「Voting Verifier」で、Validatorsが投票して合意を形成します。

  1. RelayerがVerifierにトランザクションデータを送信します。
  2. VerifierがValidatorsに検証をリクエストします。
  3. 各Validatorが独立して外部チェーンを確認し、投票します。
  4. 十分な投票が集まると、Verifierが署名付き承認データを作成します。

配送層:Relayers

Relayersは、送信元チェーンのイベントを検知し、Axelar Networkにデータを転送する非カストディアルサービスです。 また、承認済みメッセージを受信先チェーンのGatewayに配送する役割も担います。

Relayerの役割

  • イベント検知とデータ転送

送信元チェーンのGatewayが発行するイベントを監視し、トランザクションデータをAxelar NetworkのVerifier Contractに送信します。 これにより、Validatorsの検証プロセスが開始されます。

  • 承認済みメッセージの配送

Validatorsの検証が完了すると、Relayerは署名付き承認データを受信先チェーンのGatewayに送信します。 Relayersは許可不要なため、誰でもRelayerとして動作できます。

General Message Passing(GMP):クロスチェーンメッセージングの仕組み

AxelarのGeneral Message Passing(GMP)は、異なるブロックチェーン上のスマートコントラクト間で関数を呼び出すことを可能にします。 2つのパターンがあります。

  • Simple calls(シンプルな呼び出し)

受信先チェーンのコントラクトに任意のメッセージを送信します。 例えば、Avalanche上のコントラクトからBase上のコントラクトの関数を呼び出すことができます。

  • Calls with tokens(トークン付き呼び出し)

トークン転送と同時にメッセージを送信します。 トークンを受信先チェーンに転送しながら、特定の関数を実行する指示を含めることができます。

メッセージフロー全体

AxelarのGMPフローは、3つのステージに分けられます。

Stage 1: Source Chain(送信元チェーン)

送信元チェーンでメッセージを作成し、ガス代を支払うまでの流れです。

sequenceDiagram
    participant User as User/dApp
    participant Gateway1 as Gateway<br/>(送信元)
    participant GasService as Gas Service

    Note over User,GasService: Stage 1: 送信元チェーンでの処理

    User->>GasService: 1. ガス代事前支払い
    Note right of GasService: バリデーター合意と<br/>受信先実行のための手数料

    User->>Gateway1: 2. callContract() / callContractWithToken()
    Note right of Gateway1: クロスチェーン呼び出しを<br/>Gatewayに送信

    Gateway1->>Gateway1: 3. イベント発行
    Note right of Gateway1: ContractCallイベントまたは<br/>ContractCallWithTokenイベント

    Note over User,GasService: Stage 1 完了<br/>Axelarネットワークでの処理を待機

Stage 2: Axelar Network(Axelarネットワーク)

Axelarネットワーク上でバリデーターが合意を形成するまでの流れです。

sequenceDiagram
    participant Relayer as Relayer
    participant Verifier as Verifier Contract
    participant Validators as Validators
    participant Router as Message Router

    Note over Relayer,Router: Stage 2: Axelarネットワークでの処理

    Relayer->>Verifier: 1. トランザクションデータ送信
    Note right of Verifier: 送信元チェーンの<br/>イベント情報を転送

    Verifier->>Validators: 2. 検証リクエスト
    Note right of Validators: バリデーターに<br/>投票を依頼

    Validators->>Validators: 3. 投票・合意形成
    Note right of Validators: 複数のバリデーターが<br/>トランザクションを確認

    Validators->>Verifier: 4. 投票結果
    Note right of Verifier: 署名付き承認データを<br/>作成

    Verifier->>Router: 5. 承認データ準備
    Note right of Router: リレイヤーサービスが<br/>取得できるように準備

    Note over Relayer,Router: Stage 2 完了<br/>受信先チェーンでの実行を待機

Stage 3: Destination Chain(受信先チェーン)

受信先チェーンでメッセージが実行されるまでの流れです。

sequenceDiagram
    participant Relayer as Relayer
    participant Gateway2 as Gateway<br/>(受信先)
    participant Receiver as Receiver Contract

    Note over Relayer,Receiver: Stage 3: 受信先チェーンでの処理

    Relayer->>Gateway2: 1. バリデーター署名送信
    Note right of Gateway2: 承認データを<br/>受信先Gatewayに送信

    Gateway2->>Gateway2: 2. 署名検証
    Note right of Gateway2: バリデーターの署名を<br/>確認

    Gateway2->>Receiver: 3. _execute() 実行
    Note right of Receiver: 受信者コントラクトの<br/>_execute()関数を呼び出し

    Receiver->>Receiver: 4. ビジネスロジック実行
    Note right of Receiver: アプリケーションの<br/>処理を実行

    Note over Relayer,Receiver: Stage 3 完了<br/>クロスチェーンメッセージング完了

フローのまとめ

  1. 送信元チェーンでの処理(Stage 1) ユーザーがGas Serviceにガス代を事前に支払い、Gatewayの callContract() または callContractWithToken() を呼び出します。 Gatewayがイベントを発行し、Relayerが検知します。
  2. Axelarネットワークでの処理(Stage 2) RelayerがトランザクションデータをVerifier contractに送信します。 バリデーターが投票して合意を形成し、署名付き承認データを作成します。 Message Routerが承認データをRelayerサービスが取得できるように準備します。
  3. 受信先チェーンでの処理(Stage 3) Relayerがバリデーター署名を受信先Gatewayに送信します。 Gatewayが署名を検証し、受信者コントラクトの _execute() 関数を実行します。

トークン転送の仕組み:Interchain Token Service(ITS)

Axelarのトークン転送は、Interchain Token Service(ITS)を通じて実現されます。 ITSは、複数のブロックチェーンで一貫したアイデンティティを維持するトークンの作成と転送を可能にします。

ITSの特徴

ITSでは、複数のブロックチェーンでトークンの正規版を持ち、すべて単一のアドレスを共有できます。 トークンはネイティブトークンの特性を維持しながら、各チェーンでシームレスに動作します。

トークン転送のメカニズム

トークン転送は、GMPのcallContractWithToken()を使用して実行されます。 送信元チェーンでトークンをBurn(バーン)またはLock(ロック)し、受信先チェーンでMint(ミント)またはRelease(解放)します。

ITSは、新しいトークンの発行と既存トークンの統合の両方をサポートしており、開発者は用途に応じて適切な方式を選択できます。

Wormhole

次にWormholeについて説明していきます。 Wormholeは、異なるブロックチェーン間で安全かつ効率的な通信を可能にする汎用メッセージパッシングプロトコルです。

Wormholeの3層アーキテクチャ

Wormholeは、メッセージングの役割を3つの独立した層に分離しています。 この分離により、検証とメッセージ配送を独立して実行できます。

層 名称 役割
メッセージング層 Core Contract, VAA メッセージの発行・検証・実行
検証層 Guardians (19ノード) メッセージの正当性検証とVAA署名
配送層 Relayers VAA の受信先チェーンへの配送

アーキテクチャの全体像

Wormholeのアーキテクチャを図で表すと以下のようになります。

graph TB
    subgraph "Source Chain"
        Emitter[Emitter Contract<br/>メッセージ発行元]
        CoreContract1[Core Contract<br/>メッセージ発行]
        TxLogs[Transaction Logs<br/>ブロックチェーン記録]
    end

    subgraph "Guardian Network"
        Guardian1[Guardian 1]
        Guardian2[Guardian 2]
        GuardianN[Guardian 19]
        Consensus[Consensus<br/>13/19 署名]
    end

    subgraph "Infrastructure Layer"
        Spy[Spy Daemon<br/>メッセージ監視]
        Relayer[Relayers<br/>メッセージ配送]
    end

    subgraph "Destination Chain"
        CoreContract2[Core Contract<br/>VAA検証]
        Receiver[Receiver Contract<br/>メッセージ処理]
    end

    Emitter -->|1. publishMessage| CoreContract1
    CoreContract1 -->|2. Emit Event| TxLogs
    TxLogs -.->|3. Observe| Guardian1
    TxLogs -.->|3. Observe| Guardian2
    TxLogs -.->|3. Observe| GuardianN
    Guardian1 -->|4. Sign| Consensus
    Guardian2 -->|4. Sign| Consensus
    GuardianN -->|4. Sign| Consensus
    Consensus -.->|5. Broadcast VAA| Spy
    Spy -->|6. Distribute| Relayer
    Relayer -->|7. Submit VAA| CoreContract2
    CoreContract2 -->|8. Verify & Execute| Receiver

各コンポーネントは独立しており、検証(Guardians)、配送(Relayers)、実行(Core Contracts)が明確に分離されています。

メッセージング層:Core Contract と VAA

Core Contractの役割

Core Contractは、各ブロックチェーンにデプロイされるWormholeの基盤コントラクトです。 送信元チェーンでは publishMessage() 関数でメッセージを発行し、受信先チェーンでは受信したVAAの署名を検証します。

  • 送信元チェーンでの処理

アプリケーションが publishMessage() を呼び出すと、Core Contractはメッセージをトランザクションログに記録します。 このログをGuardiansが監視し、メッセージの検証を開始します。

  • 受信先チェーンでの処理

RelayerがVAAを送信すると、Core ContractはGuardianの署名を検証します。 13/19以上の有効な署名があれば、メッセージを受信先のアプリケーションに配送します。

VAA: Wormholeのメッセージフォーマット

VAA(Verifiable Action Approval)は、Guardiansが署名した検証済みメッセージです。 WormholeのBridge機能において、すべてのクロスチェーン通信はVAAを通じて実行されます。

  • VAAの構成要素

VAAは、Header(署名情報) と Body(メッセージ内容) の2つの部分で構成されます。

Headerには、Guardianの署名コレクションが含まれます。 各署名は、Guardianのインデックスと65バイトのECDSA署名データで構成され、13個以上の署名(13/19の閾値)が必要です。

Bodyには、実際のメッセージ内容が含まれます。 主要な情報は以下の通りです。

  • 送信元チェーンの識別子(例: Ethereum = 2)
  • Emitterコントラクトのアドレス(メッセージ発行元)
  • Sequence number(メッセージの連続番号で、順序を保証)
  • Payload(トークン転送情報、関数呼び出しデータなど)

VAAは (送信元チェーン, Emitterアドレス, Sequence) の組み合わせで一意に識別されます。

  • VAAの検証プロセス

受信先チェーンのCore Contractは、VAAを受け取ると以下の検証を行います。

  1. Guardianの署名が13/19以上あることを確認します。
  2. 各署名が正当なGuardianによるものかを検証します。
  3. メッセージ内容(Body)が改ざんされていないか確認します。

この検証により、送信元チェーンで確実に発行されたメッセージのみが受信先で実行されることを保証します。

検証層:Guardians

Guardiansは、Wormholeの検証を担う19個の独立したノードです。 各Guardianは送信元チェーンのトランザクションログを監視し、メッセージを検証してVAAに署名します。

Guardianによる検証の仕組み

  • 独立した観察

各Guardianは、他のGuardiansと独立してメッセージを観察します。 送信元チェーンのCore Contractがトランザクションログにメッセージを記録すると、すべてのGuardiansがそれを検知します。

  • メッセージの検証と署名

各Guardianは、メッセージの正当性を確認した後、メッセージ内容をkeccak256ハッシュで2回ハッシュ化したダイジェストに署名します。 この署名プロセスにより、メッセージの改ざんを防ぎます。

  • 署名の集約とVAA生成

13個以上のGuardianが署名すると、それらの署名を集約してVAAが生成されます。 このVAAが、受信先チェーンで検証可能な証明となります。

Proof-of-Authorityモデル

Wormholeは、Proof-of-StakeではなくProof-of-Authority(PoA)を採用しています。 19個のGuardianは匿名の参加者ではなく、実績のある信頼できる事業者です。

検証には13/19の署名(2/3以上の閾値) が必要です。 この設計により、複数のGuardianが同時に攻撃されても、システムの安全性が保たれます。

配送層:Relayers

Relayersは、GuardiansがVAAを生成した後、それを受信先チェーンに配送する役割を担います。 Relayersは配送タイミングに影響を与えることができますが、VAA自体はGuardianの署名で保護されているため、メッセージの内容や正当性には影響を与えられません。

Relayerの役割

  • VAA配送の自動化

GuardiansがVAAを生成すると、Relayersがそれを検知して受信先チェーンのCore Contractに送信します。 これにより、ユーザーが手動でVAAを転送する必要がなくなります。

  • トラストレスな設計

RelayersはVAAの署名を変更できないため、悪意のあるRelayerでもメッセージの内容を改ざんできません。 最悪の場合、Relayerがメッセージの配送を遅延させることはできますが、別のRelayerや手動配送で対処できます。

Relayerの種類

Wormholeでは、誰でもRelayerを運用できる許可不要なモデルを採用しています。 主に以下の2つのタイプがあります。

  • Executor Framework(共有Relayerネットワーク) 複数のRelayerプロバイダーが競争してメッセージを配送します。 アプリケーション開発者はインフラを構築せずに、このネットワークを利用できます。
  • Custom Relayers(独自Relayer) アプリケーションが独自のバックエンドサービスを構築・運用します。 バッチ処理や条件付きロジックなど、特殊なニーズに対応できます。

メッセージフロー全体

Wormholeのメッセージフローは、「送信側のプロセス」と「受信側のプロセス」に分けられます。

送信側のプロセス(メッセージ発行とGuardian検証)

送信チェーン上でメッセージを発行し、GuardiansがVAAに署名するまでの流れです。

sequenceDiagram
    participant Emitter as Emitter<br/>Contract
    participant Core1 as Core Contract<br/>(送信元)
    participant TxLogs as Transaction<br/>Logs
    participant G1 as Guardian 1
    participant G2 as Guardian 2
    participant GN as Guardian 19
    participant Consensus as Guardian<br/>Consensus

    Note over Emitter,Consensus: 送信側プロセス開始

    Emitter->>Core1: 1. publishMessage()
    Note right of Emitter: メッセージを<br/>Core Contractに送信

    Core1->>TxLogs: 2. Emit Event
    Note right of Core1: トランザクションログに<br/>イベントを記録

    par Guardians による独立した観察
        TxLogs-.->G1: 3a. Observe
        TxLogs-.->G2: 3b. Observe
        TxLogs-.->GN: 3c. Observe
    end

    Note over G1,GN: 各Guardianが<br/>独立してメッセージを検証

    par 署名の生成
        G1->>Consensus: 4a. Sign message
        G2->>Consensus: 4b. Sign message
        GN->>Consensus: 4c. Sign message
    end

    Consensus->>Consensus: 5. 署名を集約(13/19)
    Note right of Consensus: 2/3の閾値で<br/>VAA生成

    Note over Emitter,Consensus: 送信側プロセス完了<br/>VAA生成済み

受信側のプロセス(VAA配送と実行)

GuardiansがVAAを生成し、受信チェーンで実行されるまでの流れです。

sequenceDiagram
    participant Consensus as Guardian<br/>Consensus
    participant Spy as Spy Daemon
    participant Relayer as Relayer
    participant Core2 as Core Contract<br/>(受信先)
    participant Receiver as Receiver<br/>Contract

    Note over Consensus,Receiver: 受信側プロセス開始

    Consensus-.->Spy: 1. Broadcast VAA
    Note right of Spy: VAAsをネットワークに<br/>ブロードキャスト

    Spy->>Spy: 2. VAA監視・配布
    Note right of Spy: リアルタイムで<br/>VAAsを追跡

    Spy->>Relayer: 3. VAA取得
    Note right of Relayer: Relayerが<br/>VAAsを取得

    Relayer->>Core2: 4. Submit VAA
    Note right of Core2: 受信先チェーンに<br/>VAA送信

    Core2->>Core2: 5. Guardian署名検証
    Note right of Core2: 13/19署名を<br/>確認

    Core2->>Receiver: 6. Execute message
    Note right of Receiver: 検証済みメッセージを<br/>実行

    Receiver->>Receiver: 7. ビジネスロジック実行
    Note right of Receiver: アプリケーションの<br/>処理を実行

    Note over Consensus,Receiver: 受信側プロセス完了<br/>クロスチェーンメッセージング完了

フローのまとめ

  1. 送信元チェーンでの処理 EmitterコントラクトがCore Contractの publishMessage() を呼び出します。 Core Contractがトランザクションログにイベントを記録し、19個のGuardiansが独立して観察します。 各Guardianがメッセージに署名し、13/19の署名が集まるとVAAが生成されます。
  2. VAA配送 Guardian ConsensusがVAAをネットワークにブロードキャストします。 Spy DaemonがVAAを監視・配布し、RelayersがVAAを取得します。
  3. 受信先チェーンでの処理 Relayerが受信先チェーンのCore ContractにVAAを送信します。 Core ContractがGuardian署名を検証し、検証済みメッセージをReceiverコントラクトで実行します。

トークン転送の仕組み

Wormholeのトークン転送は、VAA内のPayloadにトークン情報を含めることで実現されます。 主にNative Token Transfers(NTT) とWrapped Token Transfers(WTT) の2つの方式があります。

Native Token Transfers(NTT)

送信元チェーンでトークンをBurnし、受信先チェーンで新しいトークンをMintします。 トークンの所有権はユーザーが保持し、各チェーンでネイティブトークンとして機能します。 DeFiガバナンストークンやステーブルコインなど、トークンの制御を保持したい場合に適しています。

Wrapped Token Transfers(WTT)

送信元チェーンでトークンをLockし、受信先チェーンでラップされたトークンをミントします。 元のトークンはWormholeのコントラクトに保管され、受信先チェーンではラップされた形で使用されます。 Portal Bridgeがこの方式の代表的な実装で、コンシューマーアプリやゲームなどで広く使用されています。

両方の方式とも、VAA内のPayloadにトークン情報(トークンアドレス、転送量、受信者アドレスなど)が含まれ、受信先チェーンのCore ContractがこのPayloadを解釈してトークン転送を実行します。

IBC

IBCについては以下の記事がわかりやすかったので、こちらを読むのが理解しやすいです。

ICM

次にICMについてまとめていきます。 ICMはAvalanche上で提供されているクロスチェーン通信プロトコルです。

詳細は以下の記事にまとめているので、よければこちがを呼んでください。

ICTT

次にICTTについてまとめていきます。 ICTTはICMを使用して、ネイティブトークンやERC20トークンをAvalanche上で作成されたL1チェーン上で相互にやり取りする仕組みです。

詳細は以下の記事にまとめているので、よければこちがを呼んでください。

Bridgeの課題

前章では、主要なBridgeサービスについて紹介してきました。 それぞれ異なった仕組みでトークンやメッセージをBridgeさせていました。 しかし、Bridgeにはいくつか課題があります。 この章ではその課題について説明していきます。

単一トランザクションで完了しない

これは当たり前ですが、複数のブロックチェーンにメッセージを送るため、少なくとも2つのトランザクションが必要になります。

単一トランザクションで完了しないの図

2つのブロックチェーン間でメッセージを送信する場合は、まず送信元チェーン上でリクエストを送るトランザクションが1つ必要です。 さらに、それをRelayerが監視してメッセージのリクエストを拾い、宛先チェーン上にメッセージを渡すトランザクションが1つ必要です。 もし1トランザクションで処理が完了する場合、リクエストを出す送信元チェーンのコントラクト内で、他のブロックチェーン上にメッセージを送付し、そのメッセージが届いて処理が完了したことを確認して後続の処理をすることができます。 これができない主な理由は3つあります。

コンセンサスアルゴリズムが異なる

ブロックチェーンはそれぞれ独立したコンセンサスアルゴリズムを持っています。 そのため、チェーンAのトランザクションが「いつ・どの条件で・いつ確定したとみなせるか」を、チェーンBが自律的に判断することはできません。

仮に同じコンセンサスアルゴリズムを採用していたとしても、そのコンセンサスはあくまでチェーン内部でのみ成立するものです。

バリデータが異なる

仮に同じコンセンサスアルゴリズムを採用していても、バリデータ集合はチェーンごとに完全に別です。 そのため、チェーンAのバリデータが「正しい」と合意したブロックは、チェーンBにとっては何の拘束力も持たないということです。

他チェーンのファイナリティを待てない

仮に、「チェーンAのトランザクション実行中に、チェーンBの処理完了を待つ」ことができる場合を考えてみます。 これはブロックチェーンにとって致命的です。 ブロック生成には以下の前提があります。

  • 一定時間内に完了すること
  • ローカルに検証可能な情報のみを使うこと
  • 外部ネットワークの応答に依存しないこと

もしブロック生成時に、他チェーンのブロック生成やそのファイナリティを待つ必要があると、他チェーンのブロック生成感覚やファイナリティに依存することになり、確認元のチェーンのブロック生成が破綻します。 また、意図的に遅延させるなどの攻撃ができてしまいます。

処理時間が長くなってしまう

通常、そのブロックチェーン上でのトランザクションの場合、そのトランザクションの完了を確認してフロントエンドなどで表示の出し分けなどをします。 しかし、他ブロックチェーン上にメッセージを送る場合、そのブロックチェーン上でのブロック生成とファイナリティを待つ必要があります。 仮にファイナリティに達する前に処理を進めてしまうと、宛先チェーン上でreorgが起きてチェーンが巻き戻る可能性があります。 そのため、しっかりファイナリティに達するのを待ってから後続の処理を進める必要があります。

そのため、数時間から数日かかることがよくあります。 特にEthereum L2系のチェーンの場合、トランザクションに対してのチャレンジ期間(異議申し立てができる期間)が7日あるため、その確定を待つ必要があります。

関数呼び出し時の追加情報

他ブロックチェーンにメッセージを送付する場合、通常以下のような情報を渡す必要があります。

  • どのブロックチェーンに送付するのか
  • どのコントラクト宛のメッセージか
  • ユニークな識別子(同じメッセージの送付を防止)
  • どのブロックチェーンからのメッセージか
  • どのコントラクトからのメッセージか

例えば、NFTやERC20トークンを送付する場合、最低でも「どのブロックチェーンに送付するのか」と「どのコントラクト宛のメッセージか」は指定する必要があります。 それ以外の情報はコントラクト内でメッセージ送付時に自動で割り当てることができますが、この2つの情報に関してはリクエストを送るユーザー側で指定してもらわないと、どこにそのメッセージを送れば良いかわからなくなってしまいます。

これにより、NFTやERC20トークンとの完全な互換性が失われます。 例えば、他ブロックチェーン上のコントラクトにNFTやERC20トークンを送付したい場合、通常の safeTransferFrom(from, to, tokenId) 関数の引数に「どのブロックチェーンに送付するのか」と「どのコントラクト宛のメッセージか」を付け足す必要があります。 そのため、Bridge機能を使いたい他のコントラクトやDappは機能を追加する必要がでてきます。

セキュリティリスク

Bridgeは攻撃されるリスクがあり、過去にも攻撃を受けて資金を盗まれる事件が起きています。 管理者アドレスの秘密鍵管理の不備やBridgeコントラクトの脆弱性、メッセージの検証不足などが原因で資金が流出しています。

理想のBridge

前章でBridgeの課題について説明してきましたが、ではその課題を解決するような理想のBridgeは作れるのか考えていきたいと思います。

コントラクト内からhttpリクエスト

コントラクトを作ったことがあるエンジニアであれば、誰しもは一度は「コントラクトからhttpリクエストができたら良いのに」と思ったことがあるはずです。 コントラクトからhttpリクエストができると、他チェーン上での処理結果を取得することができるので、1トランザクションでBridge処理を完結させることができます。 これができたら最高ですがそれはできません。

理由としては、「コントラクトの実行は決定的」である必要があるためです。 つまり、同じトランザクションは、すべてのノードで完全に同じ結果になる必要があります。 例えば、もしノードでのトランザクションの検証時に、コントラクト中でhttpリクエストを送信してチェックしていると、ノードの検証ごとにhttpリクエストが飛びます。 この時、仮にサーバーがなんらかの理由で落ちてしまったり、リクエストごとになぜか結果が異なるということが起きる可能性があります。 そうなると検証が成功したり失敗したりして、同一トランザクションなのに実行結果が分岐してしまいます。 他にもできない理由を挙げたらでてきますが、まず1つ大きな理由として上記が挙げられます。

処理時間の短縮

これは無理そうです。 可能性としては、ブロックチェーンのファイナリティを早めるしかないです。 ただ、簡単にファイナリティを早められるわけではないので、徐々に今後のチェーンのアップグレードに合わせて少しずつ改善されていくようなイメージです。

関数に追加の引数を持たせない

例えば、決まったチェーンの決まったコントラクトとしか通信をしない場合はこの仕組みが使用できます。 「どのブロックチェーンに送付するのか」と「どのコントラクト宛のメッセージか」が1つに決まっているので、コントラクトにその情報を書き込んでおけば良いためです。 privateチェーン同士などであればこれができる可能性がありますが、publicチェーンの場合この仕組みをとってしまうと、チェーンとコントラクトの組み合わせごとにBridgeコントラクトもしくはBridgeコントラクトができてしまうことになります。 「ブロックチェーンA上のコントラクトA」担当コントラクト、「ブロックチェーンA上のコントラクトB」担当コントラクトのようなイメージです。 組み合わせごとにコントラクトアドレスを管理する必要があるので、ルーティングコントラクトのようなものがあれば良いですが、管理コストが上がります。

まとめ

理想のBridgeは実現不可能という結論になってしまいましたが、Bridgeについて理解が深まっていたら嬉しいです。 もしかしたら今後これらの問題を解決するようなBridgeが出てくるかもしれないので、そこらへんも注力していこうと思います。 また、Chain Abstractionなどについても今後調べていきたいと思うので、また記事にまとめようと思います。

以下でもBridgeについてまとめているので、よければ見ていってください。

最後に

今回は「ブロックチェーンのBridge」について改めてまとめてみました。 以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!