[ERC5164] 異なるEVMネットワーク間でのコントラクトの実行をサポートする仕組みを理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、EVMネットワーク間での実行をサポートするインターフェースを定義する規格である**ERC5164**についてまとめていきます！

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

https://eips.ethereum.org/EIPS/eip-5164

:::note warn
**5164**は現在（2023年10月3日）では「**Review**」段階です。
:::

# 概要

この仕様は、EVM（Ethereum Virtual Machine）を使用するブロックチェーンにおいて、異なるチェーン間でスマートコントラクトが連携できる方法を定義しています。

:::note info
あるブロックチェーン上のスマートコントラクトが、別のブロックチェーン上のスマートコントラクトを呼び出すための方法を提供します。
:::

この仕様には、2つの主要な要素があります。
1つは「**メッセージディスパッチャー（Message Dispatcher）**」で、これは呼び出し元（スマートコントラクトが存在するチェーン）に存在します。
もう1つは「**メッセージエグゼキューター（Message Executor）**」で、これは受信側（スマートコントラクトが呼び出されるチェーン）に存在します。
メッセージを送信すると、メッセージディスパッチャーはそれをトランスポート層を通じてメッセージエグゼキューターに転送し、そこで実際に処理されます。
この仕様を実装する場合、両方のコンポーネントを構築する必要があります。

:::note info
この仕様は異なるブロックチェーン間でスマートコントラクトの連携を実現するための仕組みを提供しており、それはメッセージディスパッチャーとメッセージエグゼキューターという2つの部分で成り立っています。
メッセージディスパッチャーはスマートコントラクト呼び出し元にあり、メッセージを送信します。
そして、メッセージエグゼキューターはそのメッセージを受け取り、実際の処理を行います。
この方法により、異なるブロックチェーン間でスマートコントラクトのやり取りが可能となります。
:::

# 動機

多くのEthereumプロトコルは、異なるEVMベースのブロックチェーン間で情報を共有し合う必要があります。
これらのブロックチェーンには、Ethereumのスマートコントラクトがコードを実行できるようにするための「**ブリッジ**」と呼ばれる異なるブロックチェーンを結ぶ機能が存在します。
ただし、各ブリッジは異なる方法で連携し、異なるプログラムインターフェース（API）を持っています。
そのため、各プロトコルごとにブリッジを統合するカスタム作業が必要です。
また、各ブリッジはセキュリティ、処理速度、制御性など、異なる特性を持っています。

この状況を改善するために、共通の簡潔な仕様を定義することが提案されています。
この仕様に従えば、異なるEVMベースのブロックチェーン間でのコード共有や情報伝達が容易になり、コードの再利用が増え、共通のブリッジ実装が可能になります。

:::note info
異なるEthereumプロトコルは、異なるブロックチェーン間でのやり取りや情報共有が必要ですが、それが異なるブリッジを介して行われ、各ブリッジには異なる特性があります。
共通の仕様を定義することで、ブリッジ統合がスムーズに進行し、コードの再利用が増え、共通のブリッジ実装が容易になるということです。
:::

# 仕様

この仕様は、あるブロックチェーン上のスマートコントラクトが別のブロックチェーン上のスマートコントラクトに対してメッセージを送信できるようにするためのものです。
この機能を実現するには、2つの主要な役割である「**MessageDispatcher（メッセージディスパッチャー）**」と「**MessageExecutor（メッセージエグゼキューター）**」が必要です。

### MessageDispatcher（メッセージディスパッチャー）
メッセージが発信元となるブロックチェーン上に存在します。
スマートコントラクトからのメッセージを受け取り、それを対象の別のブロックチェーンに送信する役割を担当します。

### MessageExecutor（メッセージエグゼキューター）
メッセージエグゼキューターは、メッセージの実行が行われるブロックチェーン上に存在します。
この役割は、メッセージを受け取り、その内容に基づいてスマートコントラクトの操作を実行します。

## MessageDispatcherメソッド

### dispatchMessage

**MessageDispatcher（メッセージディスパッチャー）**は、メッセージの発信元となるブロックチェーン上に存在します。
このディスパッチャーの役割は、トランスポート層を介して、一つまたは複数の**MessageExecutor**コントラクトにメッセージをブロードキャスト（送信）することです。

各メッセージまたはメッセージバッチごとに、一意のメッセージID（`messageId`）が生成される必要があります。
このメッセージIDは、異なるブロックチェーン間で、および異なるディスパッチャー間で一意である必要があります。
これは、`chainId`（ブロックチェーンの識別子）、`dispatcherAddress`（ディスパッチャーのアドレス）、`messageNonce`（メッセージごとに増加する整数）の3つの情報をタプルとしてまとめ、それをハッシュ関数を用いて変換することで実現されます。

:::note info
**MessageDispatcher**の主な役割は、メッセージを送信元のブロックチェーン上からトランスポート層を通じて送信し、同時に各メッセージに一意のメッセージIDを生成します。
このメッセージIDは、異なるブロックチェーンや異なるディスパッチャー間での一意性を確保するために使用され、ハッシュ関数によって生成されます。
:::

```javascript
interface MessageDispatcher {
  function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId);
}
```

```yaml
- name: dispatchMessage
  type: function
  stateMutability: payable
  inputs:
    - name: toChainId
      type: uint256
    - name: to
      type: address
    - name: data
      type: bytes
  outputs:
    - name: messageId
      type: bytes32
```

## MessageDispatcherイベント

### MessageDispatched

**MessageDispatched**イベントは、個々のメッセージがディスパッチされたときに**MessageDispatcher**から発行されます。

```javascript
interface MessageDispatcher {
  event MessageDispatched(
    bytes32 indexed messageId,
    address indexed from,
    uint256 indexed toChainId,
    address to,
    bytes data,
  );
}
```

```yaml
- name: MessageDispatched
  type: event
  inputs:
    - name: messageId
      indexed: true
      type: bytes32
    - name: from
      indexed: true
      type: address
    - name: toChainId
      indexed: true
      type: uint256
    - name: to
      type: address
    - name: data
      type: bytes
```

## MessageExecutor

送信されたメッセージやメッセージバッチを実行します。
受信側のチェーン上でメッセージを実行するために、開発者は**MessageExecutor**を実装する必要があります。

**MessageExecutor**は、同じ`messageId`（メッセージ識別子）を一度だけ実行しますが、`messageIds`の実行順序については何らかの保証を提供しません。
これは、メッセージやメッセージバッチがトランスポート層を通じて順不同で移動する可能性があるためです。

### 実行
*8MessageExecutors**は、メッセージデータをブリッジのトランスポート層で検証することを推奨しています。
これにより、受け取ったメッセージが正当なものであるかを確認できます。

**MessageExecutors**は、同じメッセージを複数回実行してはいけません。
つまり、一度実行された`messageId`は再度実行されないように制御する必要があります。

メッセージの実行に失敗した場合、**MessageExecutors**はトランザクションを巻き戻し（`revert`）ます。
これにより、メッセージが後で再試行できるようになり、問題が生じた場合にはトランザクションが元の状態に戻され、メッセージの実行が再度試みられます。

### Calldata（コールデータ）

**MessageExecutors**は、実行中の各メッセージのcalldata（コールデータ）に、（`messageId`、`fromChainId`、`from`）の情報をABI形式で追加する必要があります。
これにより、メッセージの受信側は、メッセージの送信元のチェーンと送信元の情報を検証できるようになります。

:::note info
**MessageExecutor**はメッセージを実行し、同じ`messageId`を複数回実行しないように制御し、メッセージの正当性を確保します。
メッセージ実行に失敗した場合にはトランザクションを巻き戻し、受信側でメッセージの出所情報を確認できるように情報を提供します。
:::

```javascript
to.call(abi.encodePacked(data, messageId, fromChainId, from));
```

```yaml
- name: calldata
  type: bytes
  inputs:
    - name: data
      type: bytes
    - name: messageId
      type: bytes32
    - name: fromChainId
      type: uint256
    - name: from
      type: address
```

## MessageExecutorイベント

### MessageIdExecuted

メッセージまたはメッセージ・バッチが実行された時に発行されます。

```javascript
interface MessageExecutor {
  event MessageIdExecuted(
    uint256 indexed fromChainId,
    bytes32 indexed messageId
  );
}
```

```yaml
- name: MessageIdExecuted
  type: event
  inputs:
    - name: fromChainId
      indexed: true
      type: uint256
    - name: messageId
      indexed: true
      type: bytes32
```

## MessageExecutor エラー

### MessageAlreadyExecuted

`messageId`がすでに実行された場合は処理を元に戻します。
**MessageIdAlreadyExecuted&**カスタムエラーを発行します。

```javascript
interface MessageExecutor {
  error MessageIdAlreadyExecuted(
    bytes32 messageId
  );
}
```

### MessageFailure

個々のメッセージが失敗した場合、処理を元に戻します。
**MessageFailure**カスタムエラーを発行します。

```javascript
interface MessageExecutor {
  error MessageFailure(
    bytes32 messageId,
    bytes errorData
  );
}
```

# 補足

**MessageDispatcher（メッセージディスパッチャー）**は、1つまたは複数の**MessageExecutor（メッセージエグゼキューター）**と連携することができます。
ただし、どのように連携させるかは、ブリッジの設計に依存します。
ユーザーは**MessageExecutor**のアドレスを意識することなく、`dispatchMessage（メッセージの送信）`関数を呼び出すことで、簡単にメッセージをブリッジすることができます。
また、クライアントは**MessageIdExecuted**イベントによって生成されるデータを利用して、メッセージの実行状況をトレース（追跡）できます。

一部のブリッジでは、メッセージのブリッジングにネイティブ通貨の支払いが必要な場合があります。
そのため、`dispatchMessage`関数は支払い可能な状態である必要があります。

:::note info
ユーザーがメッセージをブリッジする時に、必要に応じてネイティブ通貨を支払うことができるように設計されています。
:::

# 後方互換性

この仕様は、シンプルなクロスチェーン実行を提供するため、既存のガバナンス・システムと互換性があります。

# セキュリティ考慮事項

異なるブリッジは異なるセキュリティレベルを持っています。
そのため、ユーザーはブリッジを選ぶ際に、そのブリッジがどのように実装されているかを理解する必要があります。
ブリッジのセキュリティは、その実装方法に依存するため、実装によって信頼性が変わる可能性があります。

:::note info
ブリッジを利用する際には、そのブリッジのセキュリティについて慎重に考慮する必要があります。
異なるブリッジは異なる信頼性を持っており、ユーザーは信頼性の高いブリッジを選ぶことが重要です。
ブリッジのセキュリティは実装に依存するため、ユーザーはブリッジの実装方法を理解し、セキュリティについて正確な情報を得ることが大切です。
:::

# 引用

Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Chris Whinfrey (@cwhinfrey), "ERC-5164: Cross-Chain Execution [DRAFT]," Ethereum Improvement Proposals, no. 5164, June 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5164.

# 最後に

今回は「EVMネットワーク間での実行をサポートするインターフェースを定義する規格である**ERC5164**」についてまとめてきました！
いかがだったでしょうか？

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

[Twitter @cardene777](https://twitter.com/cardene777)

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58