The Enterprise Ethereum Alliance (EEA) Distributed Ledger Technology Interoperability Specification aims to establish a secure and efficient framework for interoperability between different blockchain networks, focusing on enterprise applications. This specification addresses the need for various blockchain platforms to interact and transact seamlessly, especially in complex and regulated sectors like financial services and supply chain management. The specification includes architectural guidelines, protocol stack, and interfaces definitions, which are crucial for asset and data exchange across different blockchain systems, thereby enhancing their functionality and utility.
It is designed to support enterprise blockchain networks using diverse underlying technologies (for example EVM and non-EVM networks), facilitating complex multi-chain ecosystem deployments involving assets, payments, and securities transactions. The open standard prevents fragmentation across different vendor implementations. Use cases include currency exchanges between blockchains with different tokens, coordinating securities transfers with payment transfers on different chains, and atomic swaps/transfers of assets. The specification aims to support regulated enterprise use cases that require interoperability between multiple blockchains with secure guarantees.
This document is produced and maintained by the EEA Inc.'s Crosschain Interoperability Working Group. This version is a Public Exposure Draft for comment, and we welcome feedback to help improve it, and to help the Working Group understand how it has been useful in specific cases.This is a working document. It is inappropriate to cite this document other than as a work in progress. Please send any comments to the editors of this document.
This document is available under the terms of the Apache License 2.0. When referencing this document, the following citation format should be used:
[eeaciw-crosschainspec-v1.0] EEA CIW - Distributed Ledger Technology Interoperability Specification Draft Version 1.0. Edited by Weijia Zhang, Marelize Kriel, Anais Ofranc. 28 February 2022. EEA CIW. https://entethalliance.github.io/crosschain-interoperability/draft_crosschain_techspec.html .
Github Issues are preferred for discussion of this document.
The architecture of the crosschain interoperability stack provides a standardized framework to enable secure and seamless communication across diverse blockchain and/or DLT networks. It consists of a modular, plug-and-play architecture spanning three layers - Crosschain Applications, Crosschain Function Calls, and Crosschain Messaging. The layered architecture allows for the integration of components from different vendors, serving as a robust infrastructure for an array of crosschain applications.
Figure 1: Crosschain Protocol - Layered Architecture
Together, these three layers form a protocol stack that is robust, versatile, and capable of addressing the complex needs of modern enterprises. By adhering to the Distributed Ledger Technology Interoperability Specification defined by the Enterprise Ethereum Alliance (EEA), the protocol stack ensures that the components are not only interoperable but also scalable and secure. The EEA’s specification is a formal definition of the implementation requirements for Enterprise Ethereum clients to achieve secure and scalable crosschain communications.
The primary focus of the current specification is on Ethereum Virtual Machine (EVM) compatible blockchains, reflecting the widespread adoption and development within the Ethereum ecosystem. The specification is however implementable and compatible with other architectures, such as ZKP compatible networks and R3 Corda, where network state updates can be verified with an EEA-compliant proof. This broad-minded approach indicates a commitment to future-proofing interoperability standards and catering to an evolving digital ecosystem.
The Crosschain Messaging layer establishes the critical information infrastructure to enable trusted interoperation between blockchain or DLT networks.
This section describes the required protocols, message formats, and mechanisms for one network to prove the integrity and authenticity of data to another. This includes specifics around event-based, state-based, and transaction-based messaging to cater to diverse crosschain information exchange requirements. Additionally, the section covers format standards for crosschain proofs and considerations for secure transmission of verified data between networks.
This section describes various mechanisms through which messages are relayed from one blockchain to another, ensuring that the information can be trusted. This is fundamental to the operation of crosschain functions because the reliability and security of crosschain operations depend on the integrity of the message relay methods.
Understanding the strengths and weaknesses of various approaches is setting an important context for later understanding the technical requirements around event-based, state-based, and transaction-based messaging.
A crosschain message can be categorized, based on the underlying network characteristic, as listed below:
pragma solidity >=0.8; interface ICrosschainVerifier { function decodeAndVerify( uint256 networkId, bytes calldata encodedInfo, bytes calldata encodedProof ) external view returns ( bytes memory decodedInfo ); }
The function decodeAndVerify
is used to decode and verify message information originating from different networks.
Parameters:
networkId (required)
: Identification of the source network where the message originated. This can be used to determine the applicable verification scheme.encodedInfo (required)
: ABI-encoded message containing event, state or transaction data that needs to be decoded and verified according to the message category.
encodedProof (required)
: ABI-encoded proof information and/or signatures that an implementation can use to verify the information given in encodedInfo
. The sections below detail the format of this value.Returns:
decodedInfo
: The decoding scheme specific data as decoded from encodedInfo
and attested to by encodedProof
.The function (optionally) returns decoded information for practical purposes. To improve efficiency and to avoid another smart contract call to a verifier contract implementation that performs scheme specific decoding of event data.
If the verification fails for any reason, the verification failure must prevent any state changes from occurring.
Some example failure cases requiring a transaction revert are:
In an EVM-based environment, event attestation is a process that utilizes the block header's receipt root to prove that events occurred within that particular block. This receipt root is a cryptographic commitment to the list of transaction receipts, which in turn contain the smart contract event logs. The event data would typically contain information to trigger an action on the destination network.
Whenever a smart contract emits an event on the source network, the associated log data is written and recorded in a transaction receipt. Receipts are hashed and represented as leaves in a Merkle tree of which root is called the receipt root. This receipt root is included in each block header and used when verifying Merkle tree membership of a specific receipt containing a specific event log. This serves as a reliable way to prove that an event has indeed been emitted on the source network.
A verifying smart contract, designed to handle crosschain messages, deployed on the destination network can use this event data to execute a corresponding operation after verifying the authenticity of the event. The verifying contract will perform a validation check, by means of a Merkle inclusion proof, to verify that the event data is part of a transaction receipt that corresponds to a receipt root in a block header that the destination network recognizes as valid. This process ensures that only verified events from the source network can trigger actions on the destination network.
Parameters:
networkId (required)
: Identification of the source network that emitted the event. This can be
used to determine the verification scheme or to enforce the EIP 3220 format for EVMs.
encodedInfo (required)
: Packed ABI-encoding of
the local network identifier (networkId
),
the local contract address (contractAddress
) and
the remote event data (eventData
) to be verified.
encodedProof (required)
: ABI-encoded proof information and/or signatures that an implementation
can use to verify the information given in eventData
. The sections below detail the format of this value.
Returns:
decodedInfo
: The decoding scheme specific data as decoded from encodedInfo
. This can contain the verified local network identifier, contract address and function call data as decoded and interpreted from the given verifiable eventData
, if the next action to be taken is a remote function call.The process for using event attestation in the Crosschain Messaging Protocol can be summarised as follows.
decodeAndVerify
. This call
verifies that the event information did come from the source network and that the event data can be trusted.
Actions taken after decoding and verifying the message are Crosschain Function Call protocol dependant.
For EVM-based networks, state-based messaging relies on the state root in block headers to prove the current state of the source network. By conveying state roots for particular blocks alongside expected state data encodings, the destination network can verify specific account balances, contract variables, or other on-chain data stored in the source network's state tree.
For networks using ZKPs, state updates can be attested through computationally verifiable proofs validating changes to the network state. The ZKP prover can succinctly demonstrate a particular account balance or contract storage value transitioned to an expected amount from a previously verified state, while keeping state data opaque.
Block header proofs for state-based messaging can be used in exactly the same manner as in event-based messaging but unlike events in transaction receipts, the current header includes states in the past. ZKP circuit specific contracts can be used for verification of state changes for ZKP capable source networks.
Parameters:
networkId (required)
: Identification of the source network where state was updated. This can be
used to determine the applicable verification scheme.
encodedInfo (required)
: Packed ABI-encoding of
the local network identifier (networkId
),
the local contract address (contractAddress
) and
the remote storage root as key-value state data (stateData
) to be verified.
encodedProof (required)
: ABI-encoded proof information and/or signatures that an implementation can use to verify the information given in stateData
.Returns:
decodedInfo
: The decoding scheme specific data as decoded from encodedInfo
. This can contain the verified local network identifier, contract address and function call data as decoded and interpreted from the given verifiable stateData
, if the next action to be taken is a remote function call.Transaction-based crosschain messaging relies on conveying transaction information that can be used to prove that a transaction was executed on the source network. For EVM-based networks, the transaction root in block headers provides a cryptographic hash structure encapsulating all transactions included within the block. By transmitting block headers containing a desired transaction hash alongside a Merkle proof tying the transaction hash to the overall transaction root, the destination network can reliably verify the occurrence of the specific transaction on the source network.
More broadly, any network that utilizes Merkle trees or similar structures to efficiently store and verify transactions, can be used in the crosschain messaging protocol to attest that transactions having been executed in the network.
Block header proofs for transaction-based messaging can be used in exactly the same manner as in event-based messaging by making use of the transaction root. Moreover, a transaction executed in a source network, that is not EVM-based, can be wrapped in the same way with a Merkle inclusion proof or a ZKP depending on the capabilities of the source network.
Parameters:
networkId (required)
: Identification of the source network where the transaction occurred. This can be used to determine the verification scheme.encodedInfo (required)
: Packed ABI-encoding of
the local network identifier (networkId
),
the local contract address (contractAddress
)
the remote transaction data (txData
) to be verified.
encodedProof (required)
: ABI-encoded proof information and/or signatures that an implementation can use to verify the information given in txData
. The sections below detail the format of this value.Returns:
decodedInfo
: The decoding scheme specific data as decoded from encodedInfo
. This can contain the verified local network identifier, contract address and function call data as decoded and interpreted from the given verifiable txData
, if the next action to be taken is a remote function call.The process for using transaction or state attestation in the Crosschain Messaging Protocol can be applied in a similar way as event attestation for EVM compatible networks. It is also possible to construct proofs to be used for transaction or state attestation from non-EVM networks. It can be summarised as follows.
decodeAndVerify
. This call verifies that the information did come from the source network and
its contents can be trusted. Actions taken after decoding and verifying the message are Crosschain Function Call
protocol dependant.
The sections below detail the requirements for formats of proofs.
encodedProof
MUST contain an array of ECDSA signatures with metadata, with each signature tied to a unique signer address.
The encodedProof
value for a scenario requiring one or more ECDSA signatures has the format described in section 3.7.1. It contains the ABI-encoded Proof
struct as defined in that section.
pragma solidity >=0.8; struct Signature { uint256 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes32 meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
by
: The 160-bit Ethereum address derived from the 257-bit ECDSA public key of the signer.sigR
: The ECDSA signature's R value.sigS
: The ECDSA signature's S value.sigV
: The ECDSA signature's V value.meta
: The ECDSA signature's metadata containing optional information on the platform, curve and hash function that was used to create the signature.typ
: The type of proof indicating that the proof contains multiple ECDSA signatures.proofData
: Not applicable for this scheme.signatures
: An array of signatures.Note:
signatures
array should only contain signatures for unique by
values.
encodedProof
MUST contain an array of EDDSA signatures with metadata, with each signature tied to a unique signer public key.
The encodedProof
value for a scenario requiring one or more EDDSA signatures has the format described in this section.
It contains the ABI-encoded Proof
struct as defined below.
pragma solidity >=0.8; struct Signature { uint256 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
by
: The 256-bit EDDSA public key of the signer.sigR
: The EDDSA signature's R value.sigS
: The EDDSA signature's S value.sigV
: Not applicable for EDDSA signatures.meta
: The EDDSA signature's metadata containing optional information on the platform, curve and hash function that was used to create the signature.typ
: The type of proof indicating that the proof contains multiple EDDSA signatures.proofData
: Not applicable for this scheme.signatures
: An array of signatures.Note:
signatures
array should only contain signatures for unique by
values.
encodedProof
MUST contain an array of Schnorr signatures with metadata, with each signature tied to a unique signer public key.
The encodedProof
value for a scenario requiring one or more Schnorr signatures has the format described in this section.
It contains the ABI-encoded Proof
struct as defined below.
pragma solidity >=0.8; struct Signature { uint256 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes32 meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
by
: The 256-bit Schnorr public key of the signer.sigR
: The EDDSA signature's R value.sigS
: The EDDSA signature's S value.sigV
: Not applicable for Schnorr signatures.meta
: The Schnorr signature's metadata containing optional information on the platform, curve and hash function that was used to create the signature.typ
: The type of proof indicating that the proof contains multiple Schnorr signatures.proofData
: Not applicable for this scheme.signatures
: An array of signatures.Note:
signatures
array should only contain signatures for unique by
values.
encodedProof
MUST contain an array of BLS signatures with metadata, with each signature tied to a unique signer public key.
The encodedProof
value for a scenario requiring one or more EDDSA signatures has the format described in this section.
It contains the ABI-encoded Proof
struct as defined below.
pragma solidity >=0.8; struct Signature { uint512 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes32 meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
by
: The 384-bit BLS public key of the signer.sigR
: The BLS signature's alpha value.sigS
: Not applicable for BLS signature.sigV
: Not applicable for BLS signatures.meta
: The BLS signature's metadata containing optional information on the platform, curve and hash function that was used to create the signature.typ
: The type of proof indicating that the proof contains multiple EDDSA signatures.proofData
: Not applicable for this scheme.signatures
: An array of signatures.Note:
signatures
array should only contain signatures for unique by
values.
For implementations supporting Ethereum block header proofs, encodedProof
MUST contain Merkle Patricia proof data and an array of Ethereum validator signatures.
The encodedProof
value for a scenario, requiring a Merkle Patricia proof and one or more validator
signatures, has the format described in this section. It contains the ABI-encoded Proof
struct as defined below.
pragma solidity >=0.8; struct ProofData { bytes witnesses; bytes32 root; bytes32 blockHash; bytes blockHeaderMeta; } struct Signature { uint256 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes32 meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
witnesses
: The rlp-encoded sibling nodes as witnesses.root
: The block receipt, transaction or state root.blockHash
: The block hash.blockHeaderMeta
: The rlp-encoded block header metadata.by
: The 160-bit Ethereum address of the signer.sigR
: The ECDSA signature's R value.sigS
: The ECDSA signature's S value.sigV
: The ECDSA signature's V value.meta
: The ECDSA signature's metadata containing optional information on the platform, curve and hashing function that was used to create the signature.typ
: The type of proof indicating that the proof contains Ethereum block header data.proofData
: ABI encoded data. The data contained in the proof, e.g. sibling nodes, receipt root, block hash and block headers.signatures
: The array of Ethereum validator signatures.
For implementations supporting multivalued Merkle inclusion proofs, encodedProof
MUST contain a multivalued Merkle proof and an array of heterogeneous participant signatures.
The encodedProof
value for a scenario, requiring a multivalued Merkle proof and one or more heterogeneous
participant signatures, has the format described in this section. It contains the ABI-encoded Proof
struct as defined below.
pragma solidity >=0.8; struct ProofData { bytes32 root; bytes32[] witnesses; uint8[] flags; bytes32[] values; } struct Signature { uint256 by; uint256 sigR; uint256 sigS; uint256 sigV; bytes32 meta; } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
root
: The Merkle tree root.witnesses
: The Merkle multivalued proof's witnesses.flags
: The Merkle multivalued proof's flags.values
: The multivalued Merkle proof's leaves.by
: The 256-bit ECDSA public key coordinate or the 256-bit ED25519 public key of the signer.sigR
: The ECDSA/EDDSA signature's R value.sigS
: The ECDSA/EDDSA signature's S value.sigV
: The ECDSA signature's V value.meta
: The ECDSA/EDDSA signature's metadata, containing optional information on the platform, curve and hashing function that was used to create the signature.typ
: The type of proof indicating that the proof contains multivalued Merkle proof data.proofData
: The data contained in the proof, e.g. witnesses, flags, values and a root.signatures
: The array of consensus participant signatures.
For implementations supporting zero-knowledge proofs, encodedProof
MUST contain the data points required for the zero-knowledge circuit being used.
The encodedProof
value for a scenario requiring a zero-knowledge proof has the format described in section 3.7.5. It contains the ABI-encoded Proof
struct as defined in that section, including elliptic curve pairing points required for the particular zero-knowledge circuit implementation.
pragma solidity >=0.8; struct G1 { uint x; uint y; } struct G2 { uint[2] x; uint[2] y; } struct ProofData { G1 a; G2 b; G1 c; bytes32 meta } struct Proof { uint256 typ; bytes proofData; Signature[] signatures; }
Where:
a
: First elliptic curve pairing point for zero-knowledge circuit implementation.b
: Second elliptic curve pairing point for zero-knowledge circuit implementation.c
: Third elliptic curve pairing point for zero-knowledge circuit implementation.meta
: the public parameters, such as protocol name, base points and so on.typ
: The type of proof indicating that the proof contains zero-knowledge proof data.proofData
: The data contained in the proof, e.g. elliptic curve pairing points for a zero-knowledge circuit.signatures
: Not applicable for this scheme.When messages are relayed across networks, certain crosschain messaging models entail associated fees. In event-based transfers validated by third-party relayers, the relayers may charge a service fee plus cover gas costs for submitting proofs on destination chains. To enable transparency, a EstimateFee function can be exposed to return expected messaging fees based on payload details. It can accept parameters like the target network ID and estimated gas limit for proof verification. Using these inputs, it can calculate total fees comprising:
The estimated fees returned can inform source network applications on complete crosschain messaging costs denominated in the payload asset. Upon successful relaying, fees are paid to the relaying contract and distributed to participating relayers minus gas expenses. For non-relayed state-based transfers where users directly submit proofs, the function can return just the target network gas estimation required for proof verification. Enabling fee transparency and estimation guides rational messaging behavior across networks.
IEstimateGas
interface.
pragma solidity >=0.8; interface IEstimateGas { function estimateGas( uint256 networkId, uint256 gasLimit, bytes data ) external returns (uint256 gas); }
Where:
networkId
: The destination network identification.gasLimit
: The gasLimit set in the transaction.data
: The rlp encoded function and parameters.return
: The estimated gas fee.Settlement Finality: Settlement finality refers to the point at which a transaction is considered complete and irreversible by the system. Once a transaction achieves finality, it cannot be altered, undone, or disputed. This is a critical requirement for many financial and legal applications where certainty about the status of a transaction is necessary.
Probabilistic Finality: Probabilistic finality means that transactions become increasingly irreversible as more blocks are added to the chain after the block containing the transaction. The security of a transaction is based on the probability that it would be too computationally expensive for an attacker to change the transaction, as they would need to redo the work of the block containing the transaction and all subsequent blocks. Probabilistic finality usually occurs in DLTs that use consensus mechanisms with potential forks, like proof-of-work blockchains.
In industries heavily regulated and requiring clear audit trails (like finance and law), settlement finality is often a legal requirement. Systems operating in these domains must ensure their technology can meet these regulatory standards, making probabilistic finality unsuitable.
In addition, for DLT systems that interact with traditional banking systems or other blockchains, having clear settlement finality can simplify the process of reconciling transactions across different systems, which might have different rules regarding transaction finality.
Implementations must carefully navigate the unique characteristics of the DLT networks on which they operate, ensuring that messages and transactions maintain their integrity and legal standing as they traverse across different ledger systems, and must account for the potential variations in the time to finality and the economic implications of the consensus mechanisms. For instance, in the context of public permissionless networks utilizing Proof of Stake (PoS) for consensus, such as Ethereum post-Merge, the network offers a form of deterministic finality through mechanisms like the Casper Finality Gadget, where transactions are considered final and irreversible after a certain number of confirmations, backed by economic stakes of validators. This ensures that transactions cannot be reversed without significant financial penalties, providing a stronger assurance of finality compared to probabilistic finality seen in proof-of-work systems.
In permissioned networks with consensus mechanisms like proof of authority, immediate deterministic finality can be assumed for chain transactions with sufficient validator signatures. Byzantine fault tolerance provides strong cryptographic guarantees against block reversions. Thus, messaging finality can directly track on-chain settlement without probabilistic relaxations on permissioned environments using consensus algorithms with instant finality. Deferred effects should only be needed in cases of detected validation failures, not due to probabilistic uncertainties.
Some crosschain messaging techniques rely on cryptographic signatures to attest to the validity of data sourced from external networks. Destination networks will typically maintain a list of authorized public keys alongside verification rules in their verifier contracts. These contracts will also govern permissions to add or remove trusted keys from the whitelist based on the sender's address.
For ECDSA signatures, verifier contracts may opt to rather store Ethereum addresses, serving as proxies for actual public keys, to be used later in verifying signed payloads. Before registering their signing address, participants must validate possession of the associated private key for signing messages, usually via an eth_sign or EIP-191 personal_sign operation. Failing to properly validate the signing identity can bind an invalid address incapable of producing signatures to manage the registrant’s listing. This prevents erroneously registered entities from later participating in their own removal or updating their registered signing keys. Careful key management ensures signature-based messaging can flexibly propagate while preventing spam and manipulation.
ZK-SNARKs are succinct non-interactive arguments of knowledge that enable privacy-preserving proofs of computation and integrity. They are well-suited for crosschain messaging due to their short, efficiently verifiable proofs. ZKP relayers are operators that generate proofs to attest to the validity of messages without revealing the content. They undergo upfront ceremonies to securely generate proving and verification keys to facilitating subsequent proof generation workflows. The verification keys are published or configured in smart contracts on the destination network in order to verify computational proofs attesting to the authenticity of ZKP-encoded messages coming from the source network.
For crosschain messaging, ZKP relayers follow bi-directional multi-party computation protocols to jointly compute proofs of events, transactions or state transitions on source networks without leaking raw data. Source network integrity assertions are encoded into the generated proofs and transmitted to ZK verifier contracts on destination networks for verification against the published verification keys.
Destination networks can host ZK verifier contracts supporting different proof circuits for multiple types of ZKP-attested crosschain messages such as asset transfers, trade settlements, identity credentials, etc. By configuring circuit-specific verifier contracts and relayers with aligned circuits, advanced ZKP-based messaging flows between networks can be supported without compromising privacy.
The Crosschain Function Calls layer enables execution of operations across networks, allowing crosschain applications to trigger and coordinate activities on multiple networks. This is the operational core of the stack, enabling functions to be executed remotely on another network. This capability is crucial for allowing synchronous workflows across networks in scenarios where actions on one network depend on the state or outcomes on another. It is this layer that orchestrates the remote execution of smart contract functions, ensuring that transactions are not only executed but done so in a manner that aligns with the overarching business logic defined in the applications layer.
Crosschain function call protocols can be atomic or non-atomic. Non-atomic protocols do not ensure consistency across networks. That is, a segment of the overall crosschain flow may occur on a source network, with associated updates, but the segment on the destination network may fail, and hence the updates would not be applied on the destination network. A crosschain flow segment could fail for any of the reasons described below. Non-atomic protocols must provide a mechanism to resolve failures such that consistency is restored.
This section defines Solidity interfaces for the Crosschain Function Call layer as used by the Crosschain Application layer.
The Remote Function Call interface allows applications to call functions remotely on other networks.
pragma solidity >=0.8; interface IRemoteFunctionCall { function outboundCall( uint256 networkId, address contractAddress, bytes calldata functionCallData ) external payable; function inboundCall( uint256 networkId, bytes calldata encodedInfo, bytes calldata encodedProof, ) external payable; event RemoteFunctionCall( uint256 indexed networkId, address indexed contractAddress, bytes functionCallData ); }
Where IRemoteFunctionCall
functions are defined as:
outboundCall
: Function that emits a RemoteFunctionCall
event, to trigger a remote function call on another network, which will only succeed if a valid EVM-based attestation proof of this event can be provided.inboundCall
: Function that executes a remote function call on the local network after a valid event, state or transaction attestation proof from another network was verified.Where outboundCall
parameters are defined as:
networkId
: Identifier of remote destination network where the function call is to be made.contractAddress
: Address of the contract on the remote destination network to which the function call is to be made.functionCallData
: The function call data that consists of the ABI-encoded function selector and input parameter data with which the remote function should be called.And inboundCall
parameters are defined as:
networkId
: Identifier of the source network that initiated the remote function call.encodedInfo
: The ABI-encoded remote source network information containing the local destination network identifier, contract address and function call data encoded in remote event, transaction or state change data that needs to be verified before executing the function call locally.encodedProof
: The ABI-encoded remote source network proof data and/or signatures that an implementation can use to verify the information given in encodedInfo
.Proofs should always be generated for a specific source and destination network as a remote function call is always intended for a particular target network. It is recommended to include the networkId
in the source network encodedInfo
that will be attested to on another network so that it cannot be tampered with and that the remote function call is executed in the intended network.
When a task is dispatched from the source network, by calling outboundCall
, a remote function call is triggered on the target network. This process should be made idempotent in the sense that if identical remote function call requests are made, with the same source network information and attestation proof, then it should only be executed once. There are various approaches to implementing this. One solution would be to store hashes of the given encodedInfo
on the target network, to ensure it is used only once to facilitate a successful remote function call execution. Another solution would be to make use of a task identifier, generated to be unique to the source network information, and to persist the identifiers of successfully executed tasks on the target network.
pragma solidity >=0.8; interface ITaskManager { event OutboundTaskExecuted( bytes32 indexed taskId, uint256 indexed networkId, address indexed contractAddress, bytes functionCallData ); event InboundTaskExecuted( bytes32 indexed taskId, uint256 indexed networkId, address indexed contractAddress, bytes functionCallData ); function genTaskId(bytes calldata data) external pure returns (bytes32 taskId); }
Where ITaskManager
events are defined as:
OutboundTaskExecuted
: Event to indicate that the outbound task was successfully executed.InboundTaskExecuted
: Event to indicate that the inbound task was successfully executed.Where OutboundTaskExecuted
parameters are defined as:
taskId
: Uniquely generated task identifier.networkId
: Identifier of remote destination network where the function call is to be made.contractAddress
: Address of the contract on the remote destination network to which the function call is to be made.functionCallData
: The function call data with which the remote function should be called.And InboundTaskExecuted
parameters are defined as:
taskId
: Uniquely generated task identifier.networkId
: Identifier of the source network that initiated the remote function call.contractAddress
: Address of the local contract to which the function call was made.functionCallData
: The function call data with which the local function was called.Remote function call components need to determine the source network and contract address that initiated the remote function call. Functions in contracts on destination networks use this information to determine if the caller is authorised to execute the function remotely. The authentication parameters are provided as hidden parameters, that exist outside the scope of the declared function parameters. The parameters are appended to the call data of a function by the function call component and extracted by the application layer.
The helper function encodeNonAtomicAuthParams
appends authentication parameters to the function selector and parameter data, and the function decodeNonAtomicAuthParams
can be used to extract the authentication parameters from the call data again.
pragma solidity >=0.8; abstract contract NonAtomicHiddenAuthParams { function encodeNonAtomicAuthParams( uint256 networkId, address contractAddress, bytes memory functionCallData ) internal pure returns (bytes memory) { return bytes.concat(functionCallData, abi.encodePacked(networkId, contractAddress)); } function decodeNonAtomicAuthParams() internal pure returns ( uint256 networkId, address contractAddress ) { bytes calldata allParams = msg.data; uint256 len = allParams.length; assembly { calldatacopy(0x0, sub(len, 52), 32) networkId := mload(0) calldatacopy(12, sub(len, 20), 20) contractAddress := mload(0) } } }
Similar to existing single network applications, the type of application authentication is application specific. Applications that checked msg.sender
in a single network context should use the decodeNonAtomicAuthParams
and decodeAtomicAuthParams
to check that the source network and source contract are authorised to call the function. In the case of atomic crosschain function calls, the application should also check that the root network of the call execution tree is authorised. Applications should limit which networks can be root networks to those networks that they have access to.
The Crosschain Application Layer orchestrates complex interactions across multiple blockchain networks, enabling business processes to seamlessly operate over distinct ledgers. This layer empowers applications to access and manipulate data across different chains, unlocking new possibilities for crosschain interoperability and collaboration. It utilizes either atomic or non-atomic crosschain function calls to facilitate remote function executions and state updates across networks.
The Crosschain Application layer is a user-interfacing layer compromising the following components:The Crosschain Application Layer enables a wide range of use cases, such as crosschain asset transfers, multi-chain decentralized applications (dApps), and interoperable decentralized finance (DeFi) protocols. By facilitating seamless interaction between different DLT networks, this layer plays a crucial role in driving the adoption and scalability of this technology.
For the Crosschain Application layer to leverage the Crosschain Messaging layer defined in this specification, a Crosschain Messaging framework or SDK should be deployed alongside the source and target networks and the protocols defined in this specification should be implemented.
By leveraging the Crosschain Messaging layer, the Crosschain Application layer enables secure and reliable communication between different DLT networks. This communication is essential for executing complex, multi-chain business processes and facilitating interoperability across diverse DLT ecosystems.
For the Crosschain Application layer to leverage the Crosschain Functional Call layer defined in this specification, a Crosschain Function Call Framework or SDK should be deployed, alongside the source and target networks, and the protocols defined in this specification should be implemented.
By leveraging the Crosschain Function Call layer, the Crosschain Application layer can support a wide range of complex, multi-chain use cases. This includes scenarios such as crosschain asset swaps, multi-chain lending and borrowing, and interoperable decentralized exchanges.
Figure 2: Crosschain Protocol - Flow Overview
Key considerations for the Crosschain Application layer include:
This requirement allows developers to implement the crosschain interoperability protocols consistently across diverse DLT ecosystems, promoting seamless integration and interaction between different networks. By providing clear mappings and adaptations of the interfaces to their respective environments, non-EVM platforms can ensure that the crosschain communication mechanisms operate predictably and reliably, regardless of the underlying technology stack.
Smart contract upgradability a critical aspect to consider in the development of a DLT interoperability implementation, particularly for long-term viability and compliance with evolving regulatory standards. Smart contracts, once deployed, are immutable on the ledger. However, financial regulations and business logic may evolve, necessitating updates to the contracts. Implementing upgradable smart contracts through proxy patterns or versioning systems allows for the modification of contract logic without changing the contract's address or deployed bytecode. This approach must be carefully designed to maintain the integrity and security of the contracts, especially in a regulated financial context. Upgradability mechanisms should include strong governance processes to control updates and prevent unauthorized changes. Additionally, considering the crosschain interoperability, the upgradability strategy should ensure consistency and compatibility across different DLT platforms, enabling seamless interaction and compliance with regulatory changes over time.
In the context of this specification, certain data types such as uint256 and bytes32 are used to define interfaces and data structures. It is important to note that these data types, while distinct, are convertible and can be transformed from one to another through simple data format conversions. Smart contracts and off-chain relays are recommended to accommodate both data types to enhance interoperability and flexibility in data handling across different DLT platforms. This approach facilitates seamless crosschain communication and operations, ensuring broader compatibility and ease of implementation within diverse DTL ecosystems. Developers should ensure that their implementations incorporate mechanisms for the straightforward conversion between these data types, where necessary, to maintain the integrity and fidelity of data as it moves across network boundaries.
[eeaciw-crosschainidentification-v1.0] EEA CIW - Crosschain Identification Specification Version 1.0. Edited by Weijia Zhang and Peter Robinson. 14 December 2020. EEA CIW. https://entethalliance.github.io/crosschain-interoperability/crosschainid.html . Latest stage: https://entethalliance.github.io/crosschain-interoperability/crosschainid.html .
The EEA acknowledges and thanks the many people who contributed to the development of this draft version of the specification.
Enterprise Ethereum is built on top of Ethereum, and we grateful to the entire community who develops Ethereum, for their work and their ongoing collaboration to helps us maintain as much compatibility as possible with the Ethereum ecosystem.