March 22, 2019
Interchain Messaging with SKALE
Hello, Everyone! Today, we’re going to be reviewing SKALE’s Interchain Messaging — the communication layer connecting SKALE Chains which allows for the transfer of digital assets and calling of smart contracts by users. This functionality is made possible through the combination of smart contracts (MessageProxy, TokenManager, and DepositBox), transfer agents, BLS signatures, and fork-less consensus.
Connecting SKALE Chains
Before we can send messages between SKALE Chains, we must first connect them using the MessageProxy smart contract that comes packaged and deployed with each chain — we can think of this in a similar way to opening a TCP connection between two servers.
Linking chains is done in a one-way fashion whereby the chain’s owner (or any owner-approved party) can approve what chains they listen to messages from. In this way, the owner of a SKALE Chain can specify which chains they listen to but cannot force another chain to listen to them — so as to prevent spamming / DoS attacks.
The approval process is simple - the target SKALE Chain's ID and public group key (stored in SKALE Manager on Mainnet) are added to the MessageProxy smart contract of the originating SKALE Chain. Once this has been committed to the SKALE Chain, it will begin listening and relaying any messages from the target SKALE Chain.
Message Transfer Agents
Each virtualized subnode in an SKALE Chain comes packaged with a Message Transfer Agent that is responsible for listening to events emitted from each connected chain’s MessageProxy smart contract and executing them. Each agent is randomly assigned to short (~5 minutes) time slots during which they must monitor all incoming messages from other chains.
To send a message to another chain, a user will send a normal transaction and include the additional metadata of the target chain’s ID and send that to own chain's MessageProxy. Once the transaction has been executed and committed to the chain, the MessageProxy will emit an event including the transaction data, the number of the block that the transaction was included in, and a nonce for the transaction representing the message counter between the sending and receiving chains.
The currently appointed agent on the receiving will receive the emitted event and cache it until it has been executed on their respective chain. This agent will also be responsible for providing the signed block from the originating chain of that transaction to its virtualized subnode so that it can be verified and stored on the respective chain’s MessageProxy.
Before forwarding transactions to their respective destination smart contracts, the MessageProxy will verify that the chain has been connected, that the transactions are included in each of the signed blocks that the agent included, and that the signers of the transaction are indeed the current validators for the originating chain. Transactions not meeting these criteria will be discarded.
After the transaction has been executed on the receiving chain, it will be noted on the MessageProxy of that chain so that the next agent does not try to execute it, as well.
Note: It should be noted that SKALE Chains have no gas, allowing for fee-less transactions to be executed for each virtualized subnode / transfer agent.
Presently, there is no acknowledgement for the success / failure of an interchain transaction . Users are encouraged to instead build atop the messaging layer the system that they desire. And as popular design patterns emerge, it's likely that sample implementations will be integrated into new SKALE Chain templates.
The consensus protocol shipping with each SKALE Chain is finality-guaranteeing and does not fork. This is crucial in allowing for interchain messaging, as the receiving chain needs to be certain that the transaction has been finalized on the sending chain and that there is no chance of an orphaned transaction to be relayed by a transfer agent. Below is an illustration of this issue.
In a forking protocol, the transaction we wish to include may be included in block 4b, which will be eventually orphaned due to it not being included in the longest chain. This would lead to invalid transactions being relayed across chains — allowing for things like double spending.
Users of a SKALE Chain wishing to transfer ETH into their account can deposit ETH into a pool in the DepositBox smart contract on the Mainnet at any time. Before relaying the transaction, the transfer agents will wait a finalization period (~5 minutes) to mitigate possibility that the deposit transaction is orphaned on Ethereum. After the transaction has been finalized, the currently selected agent will transfer a message to TokenManager through MessageProxy, for send the respective amount of deposited ETH to the desired account on the respective SKALE Chain.
To transfer ETH from one chain to another, a user will send a transaction to another chain which will transfer their ETH to the TokenManager on their chain. This will emit an even to the agents of the receiving chain who will then send ETH from the receiving chain's TokenManager to the recipient.
To withdraw ETH from an SKALE Chain to the Ethereum Mainnet, a user will submit a withdrawal transaction their respective chain’s TokenManager. This transaction will emit an event to the agents who will relay it and related metadata to the MessageProxy located on the Mainnet. The MessageProxy will verify that the transaction is included in the signed block and that the signers of the transaction are the current validators for the sending chain. Once this has been verified, the transaction is forwarded to the DepositBox and the specified amount is transferred to the user’s account on the Mainnet.
SKALE’s mission is to make it quick and easy to set up a cost-effective, high-performance SKALE Chains that run full-state smart contracts. We aim to deliver a performant experience to developers that offers speed and functionality without giving up security or decentralization.