Copyright © 2021-2024 Enterprise Ethereum Alliance.
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 networks, and atomic swaps/transfers of digital 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 an EEA Publication. We welcome feedback to help improve it, and to help the Working Group understand how it has been useful in specific cases.
This specification is relevant to three primary stakeholder groups:
The core content of this document is (currently) structured in several sections:
Definitions are generally provided the first time a term is used in the content, and then other uses of the defined term link to that definition.
There are appendices that collect and revise useful information such as terms defined, or good practice recommendations.
This is an EEA specification, published by the Enterprise Ethereum Alliance, Inc.
This specification is licensed by the Enterprise Ethereum Alliance, Inc. (EEA) under the terms of the Apache License 2.0. Unless otherwise explicitly authorised in writing by the EEA, you can only use this specification in accordance with those terms.
When referencing this document, the following citation format should be used:
[eeaciw-crosschainspec-v1.0] EEA CIW - Distributed Ledger Technology Interoperability Specification Version 1.0. Edited by Weijia Zhang, Marelize Kriel, Anais Ofranc. 19 September 2024. EEA CIW. https://entethalliance.org/specs/dlt-interop/v1/ .
To provide feedback, please file or comment on an issue in the DLT-Interop public Github repository or use the EEA website contact page.
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.
Having established the overall architecture and goals of the protocol stack, we will now provide the requirements for the foundational layer: the Crosschain Messaging Layer. This layer is crucial as it provides the underlying mechanisms for secure and verifiable communication between different blockchain networks. The following section will covers specifications related to protocols, message formats, and verification methods that enable trusted interoperation across diverse distributed ledger technologies.
The Crosschain Messaging layer establishes the peer-to-peer communications infrastructure that enables 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. As several cryptographic approaches are available to afford these protections, the application developer needs to take these options into consideration when developing applications which utilize the crosschain messaging functions.
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.
Setup | Mechanism | Security | Decentralization | Ease of use | Computation | Scalability |
---|---|---|---|---|---|---|
Multisig | Governance | Aggregation of Signatures | High. Admin addresses safeguarding | Depending on admin nodes. | Easy | Light |
MPC | DKG | Schnorr Signature | Group address safeguarding | Depending on validator nodes. | Easy | Light |
ZKP Snark | Trusted Ceremony | Setup dependent proof key and verfication key | Dependent on secure setup | Medium. More centralized proofer | Difficult | Heavy |
ZKP Stark | No trusted setup needed | Proof and verification independent of external parameter | High security | Medium. More centralized proofer | Difficult | Heavy |
Oracle | Collaboration | Smart Contract callback | Trusted Oracle nodes | Medium | Easy | Medium |
Light Client | Weak subjectivity | Header and state verification | High security | No additional trusted actors needed | Medium to Difficult | Medium |
Hybrid method | Dual Channel | Cross checking | Extra security | High | Medium | Medium |
A crosschain message can be categorized, based on the underlying network characteristic, as listed below:
Understanding the distinct categories of crosschain messages—whether event-based, state-based, or transaction-based—is fundamental in recognizing the complexities involved in their processing across diverse blockchain networks. To ensure these messages maintain their integrity and security during transfer, a robust verification mechanism is indispensable. This critical need for authenticating and verifying messages leads us directly to a key requirement of crosschain communication: the Crosschain verifier interface, which we will explore in section 3.3.
Message verification is done by the Crosschain Messaging layer by implementing the following interface and function.
Messaging components MUST support the Solidity ICrosschainVerifier interface described in section 3.3 Crosschain verifier interface.
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.
The transaction MUST fail if the verification fails.
Some example failure cases requiring a transaction revert are:
Solidity's require() or revert() functions SHOULD be used to revert when verification fails.
Custom error codes and messages are RECOMMENDED to provide details on the failure reason.
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.
For event-based implementations, the decodeAndVerify function of the Solidity ICrosschainVerifier interface described in section 3.3 Crosschain verifier interface MUST be used to decode and verify event information.
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.
State attestation refers to the process of verifying and certifying the correctness of a network's state at a specific point in time. The primary goal is to ensure that the data presented is accurate and has not been tampered with.
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.
For state-based implementations, the decodeAndVerify function of the Solidity ICrosschainVerifier interface described in section 3.3 Crosschain verifier interface MUST be used to decode and verify storage root information.
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 were 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.
For transaction-based implementations, the decodeAndVerify function of the Solidity ICrosschainVerifier interface described in section 3.3 Crosschain verifier interface MUST be used to decode and verify event information.
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.
Transaction-based messaging components MUST support at least one of the formats of proofs:
For implementations supporting multiple ECDSA signatures, 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.
For implementations supporting multiple EDDSA signatures, 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.
For implementations supporting multiple Schnorr signatures, 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.
For implementations supporting multiple BLS signatures, 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 multivalued Merkle proof's witnesses.flags
: The multivalued Merkle 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.
For messaging models requiring fees, components MUST support the 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 Crosschain Function Call interface allows applications to call functions remotely on other networks.
Implementations MUST support the ICrosschainFunctionCall Solidity interface described in this section.
pragma solidity >=0.8;
interface ICrosschainFunctionCall {
function outboundCall(
uint256 networkId,
address contractAddress,
bytes calldata functionCallData
) external payable;
function inboundCall(
uint256 networkId,
bytes calldata encodedInfo,
bytes calldata encodedProof,
) external payable;
event CrosschainFunctionCall(
uint256 indexed networkId,
address indexed contractAddress,
bytes functionCallData
);
}
Where ICrosschainFunctionCall
functions are defined as:
outboundCall
: Function that emits a CrosschainFunctionCall
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.
Implementations MAY choose to support the ITaskManager Solidity interface described in this section.
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.
Implementations MUST append the hidden authentication parameters to the function call data before emitting the event.
The helper function encodeAuthParams
appends authentication parameters to the function selector and parameter data, and the function decodeAuthParams
can be used to extract the authentication parameters from the call data again.
pragma solidity >=0.8;
abstract contract AuthParams{
function encodeAuthParams(
uint256 networkId,
address contractAddress,
bytes memory functionCallData
) internal pure returns (bytes memory) {
return bytes.concat(functionCallData, abi.encodePacked(networkId, contractAddress));
}
function decodeAuthParams() 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 decodeAuthParams
to check that the source network and source contract are authorised to call the function.
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 networks, unlocking new possibilities for crosschain interoperability and collaboration. It utilizes crosschain function calls to facilitate remote function executions and state updates across networks.
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:
To ensure broad applicability and compatibility with non-EVM ecosystems, implementations of this specification in environments such as WebAssembly (WASM) or other smart contract platforms MUST provide equivalent interfaces and functions that adhere to the described behavior and semantics.
While the exact syntax may differ depending on the specific programming language and runtime environment, the core functionality, input parameters, and return values SHOULD be analogous to those defined in the Solidity interfaces within this specification.
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.
Using the crosschain messaging protocol described in this specification to facilitate interoperability between a Corda network and an Ethereum network is outlined in this section.
Corda does not have events in the same sense as Ethereum but a transaction attestation proof can be constructed instead and used in the transaction-based messaging mechanism outlined in section 3.6.3 (Transaction-based Attestation). Corda transactions are split up into component groups and their SHA-256 hashes are used as Merkle tree leaves in storage. The most important of these, for a Corda transaction attestation proof, are listed below:
The contents of the outputs and commands component groups is interpreted as an instruction and translated into an EVM function and corresponding function call parameters to facilitate a remote function call from a Corda network to an EVM-based network. Translation is done by registered parameter handlers for each function that is callable via the interop protocol.
Similar to the interop between two Ethereum networks, participant on-boarding will be required to provide sufficient security in a production environment. Even for EVM-based event attestation proofs, it might be necessary to maintain a list of active validators of the source network on the target network. For example, some QBFT network implementations using contract validator selection, no longer include the list of validator addresses in their block headers.
More generally, the network verifying a proof might need to maintain a list of authorized signatories in the verifying contract. Consensus on Corda transactions are reached among participants of the transaction and a notary, instead of a global network-wide validator pool. For this reason, it is necessary to verify that the signatures provided in the proof are from authorized signatories and match up with the required signers of the transaction as provided in the signers and notary component groups.
Corda signatures can use ECDSA with the SECP256K1 or SECP256R1 curve and SHA256 hashing, or EDDSA with the ED25519 curve and SHA512 hashing. Signatures are performed over a hash of the Merkle transaction tree root and metadata containing the signature scheme that was used. Proof and signature structures must use those defined in section 3.7.6 (Multivalued Merkle Inclusion Proofs).
The interface for encoding transaction data, for use in the transaction-based messaging mechanism, is given in section 3.6.2 (Transaction-based Interface). The structure of the transaction data (txData
) is DLT specific and an example of a compliant Corda transaction attestation proof can make use of the following structures:
struct ComponentData {
uint8 groupIndex;
uint8 internalIndex;
bytes encodedBytes;
}
struct TxData {
bytes inputParameters;
string hashAlgorithm;
bytes32 privacySalt;
ComponentData[] componentData;
}
Where the ComponentData
structure contains Corda transaction component group data that needs to be decoded, hashed and checked for Merkle tree inclusion.
Properties:
groupIndex
: Global transaction component group index.internalIndex
: Internal component group index.encodedBytes
: Contains a hex-encoded component group of the Corda transaction.Where the TxData
structure contains the Ethereum function input parameters, Corda component group data and algorithmic details to verify the component group hash's inclusion in the transaction tree.
Properties:
inputParameters
: Input parameters to be verified as being included in the component data.hashAlgorithm
: Hash algorithm used in the Merkle tree. Only SHA-256 is currently supported.privacySalt
: Salt needed to compute a Merkle tree leaf.componentData
: Hashes of these components become the values we want to proof Merkle tree membership of in our multivalued proof.The ABI-encoded TxData
struct is then included in the encodedInfo
field as txData
.
This multivalued Merkle inclusion proof can alternatively be turned in a zero-knowledge proof yielding a much lower EVM verification cost, with added privacy, but resource intensive proof generation.
The interface for the crosschain verifier contract on the EVM-based network, that is verifying the Corda proof, is outlined in section 3.3 (Crosschain verifier interface). To be able to fully integrate with an EVM network, a compliant Corda app would need to provide an equivalent implementation of decodeAndVerify
to verify an EVM-based proof as listed in section 3.7 (Formats of Proofs). This evidently results in having to maintain an active list of EVM network validators, or authorised signatories, in the messaging layer of the Corda app.
For every type of Corda transaction that gets added to the interop flows, and EVM function call instruction that can be created from it, a translator needs to be registered in the messaging layer. The translator consists of a mapping between Corda output component group data and EVM input function call parameters. The inputParameters
in the TxData
structure contains the call parameters of the function we want to call remotely through the function call layer.
Both the Corda app and the EVM contract MUST implement the interface in section 4.1.1 (Remote Function Call Interface), or an equivalent thereof, to allow functions to be called remotely on other networks.
It is worth noting that the Corda network, or the generator of the Corda proof, needs to know the destination EVM network identification and target contract address before invoking inboundCall
on an EVM network. This information is not signed over as in the case of EVM event attestation, and care should be taken that the correct contract addresses are given to this layer.
The required authentication parameters in section 4.1.3 (Application Authentication Parameters) contain an additional network identification number and contract address from where the event, transaction or state change originated. This, for EVM-based networks, provide the ability to verify the source network identification and contract address against the event, transaction or state data that was signed over in an attestation proof. The most obvious way to generalize this for Corda transaction attestation proofs, at the moment, is to use the Corda network parameters. For each contract class there is a list of SHA-256 hashes, of the approved Corda app jar versions containing the contract, that is included in the Corda network parameters as whitelisted contract implementations. A Corda transaction contains a SHA-256 hash of the source network parameters from where the transaction was sent which can be used in authorisation if we onboard and maintain the Corda network parameters on the EVM-based network.
The application layer integration mainly consists of implementing Corda app and EVM smart contract flows involving specific functions that can be called remotely, via the crosschain function call layer. The underlying messaging protocol, which depends on the registered verification scheme in the crosschain messaging layer, translates a verifiable step (event, transaction or state change) in the flow, performed on a remote network, into a verified instruction that can be used to securely perform the next step in the flow on the local network. The application layer contains the business logic that gets invoked through the crosschain messaging protocol.
Any application layer implementation needs to also cater for unhappy paths in the flows to ensure assets don't get stuck without recovery. This is a requirement in any regulated financial environment. This involves cancellation of a flow, under special circumstances, to release assets by means of a remote function call and a cryptographic proof of a prerequisite step that occurred on a different network.
The existing use case using Corda and Ethereum focussed in on DvP (Delivery vs Payment) where securities reside on a Corda network and tokenized cash reside on an Ethereum network in deposit token contracts. The crosschain messaging protocol is used to facilitate remote crosschain function calls between the two ledgers, and an execution plan, involving fixed flows, was build on top of the protocol to do intraday repo trades. The approach followed a standard hold to execute approach where assets and cash are placed on hold and holds are executed after verification of proof that either the hold was placed on the other network or the hold was executed on the other network.
Using the crosschain messaging protocol described in this specification to facilitate interoperability between an Algorand network and an EVM-based network is outlined in this section.
For Algorand to be compatible with the crosschain messaging protocol discussed in this document, it must support an event, transaction or state attestation proof as specified in section 3.7.
An Algorand block contains a transaction root, which authenticates the set of transactions appearing in the block, as the root of a Merkle tree with transaction ids as leaves, in lexicographic order. This can naturally be turned into a transaction attestation proof
An Algorand state proof is a cryptographic proof of state changes that occur in a given set of blocks. State proofs are created and signed by the Algorand network itself, so they can be used to verify that a state change happened on the Algorand network. State proofs use quantum-safe Falcon keys. An EVM pre-compile for verification of this signature scheme was proposed in EIP 7592 but these signature are not currently supported.
Algorand make use of events as part of log functions, which can be used in the crosschain message protocol described in this document.
Algorand uses ED25519 signature scheme for authorization and uses SHA-512/256 hash function for creating contract account addresses from TEAL bytecode.
It is recommended to support ECDSA using the SECP256K1 curve and KECCAK-256 hashing algorithm for better verification times in the EVM.
As a source ledger, it would need to emit events. Since Algorand implements events in Log function, it is recommended that a module function is defined in Algorand that has a similar format as Solidity. The function should log the event data such as destination chain, destination smart contract address, and function data.
Messaging components MUST support an equivalent interface as described in section 3.3 (Crosschain verifier interface).
This function could be:
@app.external
def decodeAndVerify(networkId:abi.uint256, encodedInfo: abi.byte[], encodedProof:abi.byte[], output:abi.byte[] )-> Expr:
# getDecodedInfo
return output.set(decodedInfo)
The definition of networkId, encodedInfo and encodedProof are similar to the EVM definition.
To perform encoding for message info, the encoded rules of Algorand specification (https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0004.md) should be used.
Algorand should support compatible remote function call libraries to initiate a remote function call to another network and execute a remote function call locally after verifying a proof from another network as described in section 4.1.1 (Remote Function Call Interface).
On the Algorand chain, this remote function call can be defined as
@app.external
def outboundCall(networkId:abi.uint256, contractAddress: abi.address, functionCallData:abi.byte[] )-> Expr
def inboundCall(networkId:abi.uint256, encodedInfo: abi.byte[], encodedProof:abi.byte[] )-> Expr
def CrosschainCallEvent(networkId:abi.uint256, contractAddress: abi.address, functionCallData:abi.byte[] )-> Expr
Using the crosschain messaging protocol described in this specification to facilitate interoperability between a Stellar (Soroban) network and an EVM-based network is outlined in this section.
For Stellar(Soroban) to be compatible with the crosschain messaging protocol discussed in this document, it must support an event, transaction or state attestation proof as specified in section 3.7.
Stellar(Soroban) make use of events as part of env struct, which can be used in the crosschain message protocol described in this document.
Messaging components MUST support an equivalent interface as described in section 3.3 (Crosschain verifier interface).
This function could be:
pub struct Signature { by : U256, sig_r: U256, sig_s: U256, sig_v: U256, meta: BytesN<32> }
pub struct Proof { typ: U256, proof_data: Bytes, signatures: Vec
, } trait ICrosschainVerifier { fn decode_and_verify(network_id: U256, encoded_info: Bytes, encoded_proof: Bytes) -> Bytes; }
The definition of networkId, encoded_info and encoded_proof are similar to the EVM definition.
Stellar(Soroban) should support compatible remote function call libraries to initiate a remote function call to another network and execute a remote function call locally after verifying a proof from another network as described in section 4.1.1 (Remote Function Call Interface).
On the Stellar(Soroban) chain, this remote function call can be defined as
pub fn inbound_call(env: Env,
network_id: U256, //stellar chainid
encoded_info: Bytes,
encoded_proof: Bytes,
)
fn outbound_call(env: Env, network_id: U256,target_contract_address: Bytes, function_calldata: Bytes,source_contract_address: Address );
Using the cross-chain messaging protocol described in this specification to facilitate interoperability between a Cosmos network and an EVM-based network is outlined in this section; however, it does not define specific requirements for an EVM network to verify Cosmos proofs.
The Cosmos ecosystem has a built-in Inter-Blockchain Communication (IBC) mechanism that provides a communication framework based on Light Client Verification. Specifically, IBC includes specifications for Light Clients (ICS-2), primitive communication constructs such as Connections (ICS-3), Channels/Packets (ICS-4), and the underlying Commitments (ICS-23). Full details of the protocol can be found in ICS.
In the IBC framework, a Light Client plays a crucial role:
In the ICS framework, verification is divided into two main steps: 1) updateClient
and 2) verifyMembership
.
updateClient
is the function for updating the consensus state of the counterparty chain. While the specification is broadly defined, it can be implemented as a procedure to update from an old consensus state to a new height and new consensus state by vefifying a Header:
updateClient = {header: Header, prevConsensusState: ConsensusState} -> (NewHeight, ConsensusState)
Where ConsensusState = (root: CommitmentRoot, timestamp: uint64)
For more details on implementation, refer to Handling Client Messages in IBC.
verifyMembership
uses the consensus state to perform state verification. Implementations may include Merkle proof verification using accepted StateRoot. For more information on implementation, see Packet Commitment Verification in IBC.
The EEA's Crosschain Messaging Layer specification can be considered as defining the verifyMembership
step. Specifically:
encodedProof
can be viewed as an Ethereum ABI-encoded CommitmentProof
as defined in ICS-23.encodedInfo
can be considered as the encoding of the new consensus state obtained from updateClient
, along with CommitmentRoot
, CommitmentPath
, and Value
.decodeAndVerify
function can be seen as decoding these elements and internally executing verifyMembership = (root: CommitmentRoot, proof: CommitmentProof, path: CommitmentPath, value: Value) => boolean
to perform state verification.It is worth noting that the updateClient
step in ICS specifies a mechanism for verifying and accepting Header. When verifying multiple Key-Value pairs against a single Header, it is beneficial from a processing efficiency standpoint to have these as separate primitives.
For implementations seeking to align with ICS and fully integrate with the Cosmos network, ibc-solidity provides an EVM-compatible implementation of ICS components, including updateClient
and verifyMembership
.
Using the crosschain messaging protocol described in this specification to facilitate interoperability between an arbitrary DLT network, connected to the FinP2P network, and an Ethereum network is outlined in this section.
The FinP2P system uses routers, which serve as a connection point between different participants involved in the DLT ecosystem of tokenized assets. These routers utilize the open FinP2P protocol to unify user identities, wallets, data structures, messaging and orchestration of DLT transactions. They can be used as a network of trusted peers, signing receipts, to attest to finalized transactions on the underlying DLT networks they are connected to.
FinP2P receipts, when part of an execution plan, can be used to enable interoperability between two of the underlying DLT networks, by trusting router signatures over FinP2P receipts. This allows for a mode of interoperability where the heterogeneous DLT networks are less coupled. That is, the verifying DLT network doesn't need to understand the workings of the DLT network where the transaction originated. This means that EVM verifiers can be standardised around processing generic FinP2P receipts and verifying the signatures over them. This comes at the cost of an additional layer of trusted peers instead of relying on the consensus participants of the underlying DLT network.
The FinP2P network can also facilitate interoperability between two DLT networks by routing around ledger specific proofs, as part of the receipt, as is described in section 3.7 of this document. Verifier contracts handling such proofs will do so independently of whether the proofs were routed via FinP2P or not.
The FinP2P open source specifications are available here.
A signed FinP2P receipt is encoded, hashed and signed using EIP712. Using a known Receipt
structure allows the EVM verifier to extract data from the encoded structure and translate it into a command and EVM smart contract function call parameters. FinP2P receipts are defined below:
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "string"}
],
"Account": [
{"name": "accountType", "type": "string"},
{"name": "finId", "type": "string"}
],
"TransactionDetails": [
{"name": "operationId", "type": "string"},
{"name": "operationType", "type": "string"},
{"name": "transactionId", "type": "string"}
],
"Asset": [
{"name": "assetId", "type": "string"},
{"name": "assetType", "type": "string"}
],
"TradeDetails": [
{"name": "executionPlanId", "type": "string"},
{"name": "instructionSequenceNumber", "type": "string"}
],
"Receipt": [
{"name": "id", "type": "string"},
{"name": "source", "type": "Account"},
{"name": "destination", "type": "Account"},
{"name": "asset", "type": "Asset"},
{"name": "tradeDetails", "type": "TradeDetails"},
{"name": "transactionDetails", "type": "TransactionDetails"}
]
}
}
Supported FinP2P signatures use ECDSA with the SECP256K1 curve and KECCAK hashing for optimized verification in the EVM. Signatures are encoded according to the EEA specification as defined in section 3.7.1 (Multiple ECDSA Signatures) to hold FinP2P receipt signatures.
The interface for encoding transaction data, for use in the transaction-based messaging mechanism, is given in section 3.6.2 (Transaction-based Interface). The structure of the transaction data (txData
) is DLT specific and an example of a compliant FinP2P transaction attestation proof can make use of the following structures containing a FinP2P receipt:
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}
struct Asset {
string assetId;
string assetType;
}
struct TransactionDetails {
string operationId;
string operationType;
string transactionId;
}
struct TradeDetails {
string executionPlanId;
uint256 instructionSequenceNumber;
}
struct Account {
string accountType;
string finId;
}
struct Receipt {
string id;
Account source;
Account destination;
Asset asset;
TradeDetails tradeDetails;
TransactionDetails transactionDetails;
}
struct TxData {
bytes inputParameters;
string command;
EIP712.EIP712Domain domainParameters;
Receipt receipt;
}
Where the EIP712Domain
structure contains FinP2P domain parameters.
Properties:
name
: The user readable name of the signing domain.version
: Current major version of the signing domain.chainId
: The EIP-155 chain id.verifyingContract
: Destination verifying contract address.Where the Receipt
structure contains FinP2P receipt data.
Properties:
assetId
: Asset identification, e.g. ownera, USD, BTC.assetType
: Asset type, e.g. finp2p, fiat, cryptocurrency.operationId
: Operation identification on the FinP2P network.operationType
: Type of operation performed by the transaction.transactionId
: Transaction identification on the underlying DLT network.executionPlanId
: Execution plan identification corresponding to a unique trade identifier.instructionSequenceNumber
: Unique instruction number for the step of the execution plan that was performed.accountType
: Type of FinP2P account, e.g. finp2pfinId
: FinP2P account identification in the form of a compressed public key.id
: Unique receipt identification.source
: Source FinP2P account.destination
: Destination FinP2P account.asset
: Asset used in the trade.tradeDetails
: Trade details.transactionDetails
: Transaction details.Where the TxData
structure contains the Ethereum function input parameters, FinP2P receipt data and domain parameters required to recreate the hash that was signed.
Properties:
inputParameters
: Input parameters to be verified as being included in the receipt data.command
: The unique command constructed from the receipt contents that is used to make the remote function call.domainParameters
: EIP712 domain parameters needed to encode typed data.receipt
: The FinP2P receipt.The ABI-encoded TxData
struct is then included in the encodedInfo
field as txData
.
The interface for the crosschain verifier contract on the EVM-based network, that is verifying the FinP2P receipt proof, is outlined in section 3.3 (Crosschain verifier interface).
For every FinP2P receipt, pertaining to a specific execution plan instruction, that gets added to the interop flows, and EVM function call that can be created from it, a translator needs to be registered in the messaging layer. The translator consists of a mapping between receipt fields and EVM input function call parameters. The inputParameters
in the TxData
structure contains the call parameters of the function we want to call remotely through the function call layer.
EVM and non-EVM based DLT networks MUST implement the interface in section 4.1.1 (Remote Function Call Interface), or an equivalent thereof, to allow functions to be called remotely with a proof that a previous step in the execution plan was executed successfully.
The FinP2P adapter that generates the proof from the receipt needs to know the target network information, where the receipt will be verified, as well as the target contract address where the remote function call must be executed. This information is included in encodedInfo
when invoking inboundCall
on an EVM network. It is not signed over as in the case of EVM event attestation, and care should be taken that the correct contract addresses are given to this layer.
The application layer integration consists of implementing specific functions that can be called remotely, via the crosschain function call layer. The existing use case using FinP2P with Ethereum focussed in on DvP (Delivery vs Payment) where securities reside on the FinP2P network as tokenized assets and tokenized cash resides on an Ethereum network in deposit token contracts. The crosschain messaging protocol is used to facilitate remote crosschain function calls to transfer the asset in the FinP2P network and provide payment for it on Ethereum. The approach makes use of FinP2P escrow services to hold and transfer assets. Holds are executed after verification of proof that either the hold was placed on the other network or the hold was executed on the other network.
The ISO Blockchain and Distributed Ledger Technology — Interoperability Framework (ISO/CD TS 23516) identifies three interoperation modes: Data Transfer, Asset Transfer, and Asset Exchange. Data Transfer involves copying information between DLTs, potentially with intermediate processing. Asset Transfer allows for the movement of assets from one DLT to another, often involving locking or burning the original asset and creating a wrapped token on the destination network. Asset Exchange enables atomic swaps of assets between different DLTs, where participants must be present on both networks, and the assets remain on their native chains while changing ownership addresses.
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 are 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.