DCIPs/EIPS/eip-5164.md

330 lines
10 KiB
Markdown
Raw Normal View History

---
eip: 5164
title: Cross-Chain Execution
description: Defines an interface that supports execution across EVM networks.
author: Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Chris Whinfrey (@cwhinfrey)
discussions-to: https://ethereum-magicians.org/t/eip-5164-cross-chain-execution/9658
status: Review
type: Standards Track
category: ERC
created: 2022-06-14
---
## Abstract
This specification defines a cross-chain execution interface for EVM-based blockchains. Implementations of this specification will allow contracts on one chain to call contracts on another by sending a cross-chain message.
The specification defines two components: the "Message Dispatcher" and the "Message Executor". The Message Dispatcher lives on the calling side, and the executor lives on the receiving side. When a message is sent, a Message Dispatcher will move the message through a transport layer to a Message Executor, where they are executed. Implementations of this specification must implement both components.
## Motivation
Many Ethereum protocols need to coordinate state changes across multiple EVM-based blockchains. These chains often have native or third-party bridges that allow Ethereum contracts to execute code. However, bridges have different APIs so bridge integrations are custom. Each one affords different properties; with varying degrees of security, speed, and control. Defining a simple, common specification will increase code re-use and allow us to use common bridge implementations.
## Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
This specification allows contracts on one chain to send messages to contracts on another chain. There are two key interfaces that needs to be implemented:
- `MessageDispatcher`
- `MessageExecutor`
The `MessageDispatcher` lives on the origin chain and dispatches messages to the `MessageExecutor` for execution. The `MessageExecutor` lives on the destination chain and executes dispatched messages.
There are also extensions of `MessageDispatcher`, each defining a method for initiating a message or message batch:
- `SingleMessageDispatcher`
- `BatchMessageDispatcher`
Alternatively, `MessageDispatcher`s may implement a custom interface for initiating messages.
### MessageDispatcher
The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher's job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts.
A unique `messageId` MUST be generated for each message or message batch.
To ensure uniqueness, it is RECOMMENDED that a monotonically increasing nonce is used in the calculation of the `messageId`.
#### MessageDispatcher Events
**MessageDispatched**
The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched.
```solidity
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
```
**MessageBatchDispatched**
The `MessageBatchDispatched` event MUST be emitted by the `MessageDispatcher` when a batch of messages is dispatched.
```solidity
struct Message {
address to;
bytes data;
}
interface MessageDispatcher {
event MessageBatchDispatched(
bytes32 indexed messageId,
address indexed from,
uint256 indexed toChainId,
Message[] messages
);
}
```
```yaml
- name: MessageBatchDispatched
type: event
inputs:
- name: messageId
indexed: true
type: bytes32
- name: from
indexed: true
type: address
- name: toChainId
indexed: true
type: uint256
- name: messages
type: Message[]
```
### SingleMessageDispatcher
The `SingleMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessage`, for dispatching an individual message to be executed on the `toChainId`.
#### SingleMessageDispatcher Methods
**dispatchMessage**
Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`.
`SingleMessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched.
`SingleMessageDispatcher`s MUST revert if `toChainId` is not supported.
`SingleMessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`.
`SingleMessageDispatcher`s MUST use a unique `messageId` for each message.
`SingleMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message.
`SingleMessageDispatcher`s MAY require payment.
```solidity
interface SingleMessageDispatcher is 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
```
### BatchedMessageDispatcher
The `BatchedMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessageBatch`, for dispatching a batch of messages to be executed on the `toChainId`.
#### BatchedMessageDispatcher Methods
**dispatchMessageBatch**
Will dispatch a batch of messages to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`.
`BatchedMessageDispatcher`s MUST emit the `MessageBatchDispatched` event when a message batch is dispatched.
`BatchedMessageDispatcher`s MUST revert if `toChainId` is not supported.
`BatchedMessageDispatcher`s MUST forward the message batch to the `MessageExecutor` on the `toChainId`.
`BatchedMessageDispatcher`s MUST use a unique `messageId` for each batch of messages.
`BatchedMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the batch of messages.
`BatchedMessageDispatcher`s MAY require payment.
```solidity
interface BatchedMessageDispatcher is MessageDispatcher {
function dispatchMessageBatch(uint256 toChainId, Message[] calldata messages) external payable returns (bytes32 messageId);
}
```
```yaml
- name: dispatchMessageBatch
type: function
stateMutability: payable
inputs:
- name: toChainId
type: uint256
- name: messages
type: Message[]
outputs:
- name: messageId
type: bytes32
```
### MessageExecutor
The `MessageExecutor` executes dispatched messages and message batches. Developers must implement a `MessageExecutor` in order to execute messages on the receiving chain.
The `MessageExecutor` will execute a messageId only once, but may execute messageIds in any order. This specification makes no ordering guarantees, because messages and message batches may travel non-sequentially through the transport layer.
#### Execution
`MessageExecutor`s SHOULD verify all message data with the bridge transport layer.
`MessageExecutor`s MUST NOT successfully execute a message more than once.
`MessageExecutor`s MUST revert the transaction when a message fails to be executed allowing the message to be retried at a later time.
**Calldata**
`MessageExecutor`s MUST append the ABI-packed (`messageId`, `fromChainId`, `from`) to the calldata for each message being executed. This allows the receiver of the message to verify the cross-chain sender and the chain that the message is coming from.
```solidity
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
```
#### Error handling
**MessageAlreadyExecuted**
`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error.
```solidity
interface MessageExecutor {
error MessageIdAlreadyExecuted(
bytes32 messageId
);
}
```
**MessageFailure**
`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error.
```solidity
interface MessageExecutor {
error MessageFailure(
bytes32 messageId,
bytes errorData
);
}
```
**MessageBatchFailure**
`MessageExecutor`s MUST revert the entire batch if any message in a batch fails and SHOULD emit a `MessageBatchFailure` custom error.
```solidity
interface MessageExecutor {
error MessageBatchFailure(
bytes32 messageId,
uint256 messageIndex,
bytes errorData
);
}
```
#### MessageExecutor Events
**MessageIdExecuted**
`MessageIdExecuted` MUST be emitted once a message or message batch has been executed.
```solidity
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
```
## Rationale
The `MessageDispatcher` can be coupled to one or more `MessageExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `dispatchMessage` without being aware of the `MessageExecutor` address. Messages can also be traced by a client using the data logged by the `MessageIdExecuted` event.
Some bridges may require payment in the native currency, so the `dispatchMessage` function is payable.
## Backwards Compatibility
This specification is compatible with existing governance systems as it offers simple cross-chain execution.
## Security Considerations
Bridge trust profiles are variable, so users must understand that bridge security depends on the implementation.
## Copyright
Copyright and related rights waived via [CC0](../LICENSE.md).