📖 本 EIP 正处于审查阶段。它可能会有变化,希望得到反馈。

EIP-5164: Cross-Chain Execution Source

Defines an interface that supports execution across EVM networks.

作者 Brendan Asselstine, Pierrick Turelier
讨论-To https://ethereum-magicians.org/t/eip-5164-cross-chain-execution/9658
状态 Review
类型 Standards Track
分类 ERC
创建日期 2022-06-14
英文版 https://eips.ethereum.org/EIPS/eip-5164

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.

The specification defines two components: the “Cross Chain Relayer” and the “Cross Chain Executor”. The Cross Chain Relayer lives on the calling side, and the executor lives on the receiving side. Calls sent to Cross Chain Relayers will move through a transport layer to Cross Chain Executor(s), 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:

  • CrossChainRelayer
  • CrossChainExecutor

The CrossChainRelayer lives on the origin chain. Users can share a single CrossChainRelayer or deploy their own.

The CrossChainExecutor lives on the destination chain and executes relayed calls. Users can share a single CrossChainExecutor or deploy their own.

CrossChainRelayer

The CrossChainRelayer lives on the chain from which messages are sent. The Relayer’s job is to broadcast calls through a transport layer to one or more CrossChainExecutor contracts.

Methods

relayCalls

Will relay a batch of Call structs to be executed by any CrossChainExecutor(s) that execute calls from this Relayer. The gasLimit is used as a limit on the executing side.

CrossChainRelayers MUST emit the RelayedCalls event when a batch of calls is relayed.

CrossChainRelayers MUST increment a nonce so that each batch of calls can be uniquely identified.

CrossChainRelayers MUST return the nonce to allow callers to track the batch of calls.

CrossChainRelayers SHOULD pass the nonce as well as the address of the sender in the call to CrossChainExecutor to uniquely identify the message on the receiving chain.

CrossChainRelayers MAY require payment.

struct Call {
  address target;
  bytes data;
}

interface CrossChainRelayer {
  function relayCalls(Call[] calldata calls, uint256 gasLimit) external payable returns (uint256 nonce);
}
- name: relayCalls
  type: function
  stateMutability: payable
  inputs:
    - name: calls
      type: Call[]
    - name: gasLimit
      type: uint256
  outputs:
    - name: nonce
      type: uint256

Events

RelayedCalls

The RelayedCalls event MUST be emitted by the CrossChainRelayer when relayCalls is called.

interface CrossChainRelayer {
  event RelayedCalls(
    uint256 indexed nonce,
    address indexed sender,
    Call[] calls,
    uint256 gasLimit
  );
}
- name: RelayedCalls
  type: event
  inputs:
    - name: nonce
      indexed: true
      type: uint256
    - name: sender
      indexed: true
      type: address
    - name: calls
      type: Call[]
    - name: gasLimit
      type: uint256

Error handling

GasLimitTooHigh

CrossChainRelayers MAY revert with GasLimitTooHigh if the gasLimit passed to relayCalls is higher than the maximum gas limit accepted by the bridge being used.

interface CrossChainRelayer {
  error GasLimitTooHigh(
    uint256 gasLimit,
    uint256 maxGasLimit
  );
}

CrossChainExecutor

The CrossChainExecutor executes relayed calls. Developers must implement a CrossChainExecutor in order to execute messages on the receiving chain.

The CrossChainExecutor will execute a nonce only once, but may execute nonces in any order. This specification makes no ordering guarantees, because messages may travel non-sequentially through the transport layer.

Execution

CrossChainExecutors SHOULD authenticate that the call has been performed by the bridge transport layer.

CrossChainExecutors MUST NOT execute a batch of calls more than once.

Calldata

CrossChainExecutors MUST append the ABI-packed (nonce, sender) to the calldata for each call being executed. It allows the receiver of the call to check the true sender of the transaction and use the nonce to apply any transaction ordering logic.

interface CrossChainExecutor {
  bytes calldata = abi.encode(Call.data, nonce, sender); // Can also use abi.encodePacked
}
- name: calldata
  type: bytes
  inputs:
    - name: Call.data
      type: bytes
    - name: nonce
      type: uint256
    - name: sender
      type: address

Error handling

CallsAlreadyExecuted

CrossChainExecutors MUST revert if a batch of calls has already been executed and SHOULD emit CallsAlreadyExecuted custom error.

interface CrossChainExecutor {
  error CallsAlreadyExecuted(
    uint256 nonce
  );
}

CallFailure

CrossChainExecutors SHOULD revert with a CallFailure error if a call fails.

interface CrossChainExecutor {
  error CallFailure(
    uint256 callIndex,
    bytes errorData
  );
}

Events

ExecutedCalls

ExecutedCalls MUST be emitted once calls have been executed.

interface CrossChainExecutor {
  event ExecutedCalls(
    CrossChainRelayer indexed relayer,
    uint256 indexed nonce
  );
}
- name: ExecutedCalls
  type: event
  inputs:
    - name: relayer
      indexed: true
      type: CrossChainRelayer
    - name: nonce
      indexed: true
      type: uint256

Rationale

The CrossChainRelayer can be coupled to one or more CrossChainExecutor. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling relayCalls without being aware of the CrossChainExecutor address. Messages can also be traced by a client using the data logged by the ExecutedCalls event.

Calls are relayed in batches because it is such a common action. Rather than have implementors take different approaches to encoding multiple calls into the data portion, this specification includes call batching to take away any guess work.

Some bridges may require payment in the native currency, so the relayCalls function is payable.

Bridges relay messages in various ways, applications should be aware of how the bridge they rely on operates and decide if they want to enforce transaction ordering by using the nonce.

向后兼容性

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 and related rights waived via CC0.

参考文献

Please cite this document as:

Brendan Asselstine, Pierrick Turelier, "EIP-5164: Cross-Chain Execution [DRAFT]," Ethereum Improvement Proposals, no. 5164, June 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5164.