Skip to content

Latest commit

 

History

History
 
 

ics-027-interchain-accounts

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
ics title stage category requires kind author created modified
27
Interchain Accounts
Draft
IBC/TAO
25, 26
instantiation
2019-08-01
2019-12-02

Synopsis

This standard document specifies packet data structure, state machine handling logic, and encoding details for the account management system over an IBC channel between separate chains.

Motivation

On Ethereum, there are two types of accounts: externally owned accounts, controlled by private keys, and contract accounts, controlled by their contract code (ref). Similar to Ethereum's CA (contract accounts), interchain accounts are managed by another chain while retaining all the capabilities of a normal account (i.e. stake, send, vote, etc). While an Ethereum CA's contract logic is performed within Ethereum's EVM, interchain accounts are managed by another chain via IBC in a way such that the owner of the account retains full control over how it behaves.

Definitions

The IBC handler interface & IBC relayer module interface are as defined in ICS 25 and ICS 26, respectively.

Desired Properties

  • Permissionless
  • Fault containment: Interchain account must follow rules of its host chain, even in times of Byzantine behaviour by the counterparty chain (the chain that manages the account)
  • The chain that controls the account must process the results asynchronously and according to the chain's logic. The result should be 0x0 if the transaction was successful and an error code other than 0x0 if the transaction failed.
  • Sending and receiving transactions will be processed in an ordered channel where packets are delivered exactly in the order which they were sent.

Technical Specification

The implementation of interchain account is non-symmetric. This means that each chain can have a different way to generate an interchain account and deserialise the transaction bytes and a different set of transactions that they can execute. For example, chains that use the Cosmos SDK will deserialise tx bytes using Amino, but if the counterparty chain is a smart contract on Ethereum, it may deserialise tx bytes by an ABI that is a minimal serialisation algorithm for the smart contract. The interchain account specification defines the general way to register an interchain account and transfer tx bytes. The counterparty chain is responsible for deserialising and executing the tx bytes, and the sending chain should know how counterparty chain will handle the tx bytes in advance.

Each chain must satisfy following features to create a interchain account:

  • New interchain accounts must not conflict with existing ones.
  • Each chain must keep track of which counterparty chain created each new interchain account.

Also, each chain must know how the counterparty chains serialise/deserialise transaction bytes in order to send transactions via IBC. And the counterparty chain must implement the process of safely executing IBC transactions by verifying the authority of the transaction's signers.

The chain must reject the transaction and must not make a state transition in the following cases:

  • The IBC transaction fails to be deserialised.
  • The IBC transaction expects signers other than the interchain accounts made by the counterparty chain.

It does not restrict how you can distinguish signers that was not made by the counterparty chain. But the most common way would be to record the account in state when the interchain account is registered and to verify that signers are recorded interchain account.

Data Structures

Each chain must implement the below interfaces to support interchain account. createOutgoingPacket method in IBCAccountModule interface defines the way to create an outgoing packet for a specific type. Type indicates how IBC account transaction should be constructed and serialised for the host chain. Generally, type indicates what framework the host chain was built from. generateAddress defines the way how to determine the account's address by using identifier and salt. Using the salt to generate an address is recommended, but not required. If the chain doesn't support a deterministic way to generate an address with a salt, it can be generated by its own way. createAccount is used to create account with generated address. New interchain account must not conflict with existing ones, and chains should keep track of which counterparty chain created each new interchain account in order to verify the authority of transaction's signers in authenticateTx. authenticateTx validates a transaction and checks that the signers in the transaction have the right permissions. runTx executes a transaction after it was authenticated successfully.

type Tx = object

interface IBCAccountModule {
  createOutgoingPacket(chainType: Uint8Array, data: any)
  createAccount(address: Uint8Array)
  generateAddress(identifier: Identifier, salt: Uint8Array): Uint8Array
  deserialiseTx(txBytes: Uint8Array): Tx
  authenticateTx(tx: Tx): boolean
  runTx(tx: Tx): uint32
}

RegisterIBCAccountPacketData is used by the counterparty chain to register an account. An interchain account's address is defined deterministically with the channel identifier and salt. The generateAccount method is used to generate a new interchain account's address. It is recommended to generate address by hash(identifier+salt), but other methods may be used. This function must generate a unique and deterministic address by utilising identifier and salt.

interface RegisterIBCAccountPacketData {
  salt: Uint8Array
}

RunTxPacketData is used to execute a transaction on an interchain account. The transaction bytes contain the transaction itself and are serialised in a manner appropriate for the destination chain.

interface RunTxPacketData {
  txBytes: Uint8Array
}

The IBCAccountHandler interface allows the source chain to receive results of executing transactions on an interchain account.

interface InterchainTxHandler {
  onAccountCreated(identifier: Identifier, address: Address)
  onTxSucceeded(identifier: Identifier, txBytes: Uint8Array)
  onTxFailed(identifier: Identifier, txBytes: Uint8Array, errorCode: Uint8Array)
}

Subprotocols

The subprotocols described herein should be implemented in a "interchain-account-bridge" module with access to a router and codec (decoder or unmarshaller) for the application and access to the IBC relayer module.

Port & channel setup

The setup function must be called exactly once when the module is created (perhaps when the blockchain itself is initialised) to bind to the appropriate port and create an escrow address (owned by the module).

function setup() {
  relayerModule.bindPort("interchain-account", ModuleCallbacks{
    onChanOpenInit,
    onChanOpenTry,
    onChanOpenAck,
    onChanOpenConfirm,
    onChanCloseInit,
    onChanCloseConfirm,
    onSendPacket,
    onRecvPacket,
    onTimeoutPacket,
    onAcknowledgePacket,
    onTimeoutPacketClose
  })
}

Once the setup function has been called, channels can be created through the IBC relayer module between instances of the interchain account module on separate chains.

An administrator (with the permissions to create connections & channels on the host state machine) is responsible for setting up connections to other state machines & creating channels to other instances of this module (or another module supporting this interface) on other chains. This specification defines packet handling semantics only, and defines them in such a fashion that the module itself doesn't need to worry about what connections or channels might or might not exist at any point in time.

Routing module callbacks

Channel lifecycle management

Both machines A and B accept new channels from any module on another machine, if and only if:

  • The other module is bound to the "interchain account" port.
  • The channel being created is ordered.
  • The version string is empty.
function onChanOpenInit(
  order: ChannelOrder,
  connectionHops: [Identifier],
  portIdentifier: Identifier,
  channelIdentifier: Identifier,
  counterpartyPortIdentifier: Identifier,
  counterpartyChannelIdentifier: Identifier,
  version: string) {
  // only ordered channels allowed
  abortTransactionUnless(order === ORDERED)
  // only allow channels to "interchain-account" port on counterparty chain
  abortTransactionUnless(counterpartyPortIdentifier === "interchain-account")
  // version not used at present
  abortTransactionUnless(version === "")
}
function onChanOpenTry(
  order: ChannelOrder,
  connectionHops: [Identifier],
  portIdentifier: Identifier,
  channelIdentifier: Identifier,
  counterpartyPortIdentifier: Identifier,
  counterpartyChannelIdentifier: Identifier,
  version: string,
  counterpartyVersion: string) {
  // only ordered channels allowed
  abortTransactionUnless(order === ORDERED)
  // version not used at present
  abortTransactionUnless(version === "")
  abortTransactionUnless(counterpartyVersion === "")
  // only allow channels to "interchain-account" port on counterparty chain
  abortTransactionUnless(counterpartyPortIdentifier === "interchain-account")
}
function onChanOpenAck(
  portIdentifier: Identifier,
  channelIdentifier: Identifier,
  version: string) {
  // version not used at present
  abortTransactionUnless(version === "")
  // port has already been validated
}
function onChanOpenConfirm(
  portIdentifier: Identifier,
  channelIdentifier: Identifier) {
  // accept channel confirmations, port has already been validated
}
function onChanCloseInit(
  portIdentifier: Identifier,
  channelIdentifier: Identifier) {
  // no action necessary
}
function onChanCloseConfirm(
  portIdentifier: Identifier,
  channelIdentifier: Identifier) {
  // no action necessary
}

Packet relay

In plain English, between chains A and B. It will describe only the case that chain A wants to register an Interchain account on chain B and control it. Moreover, this system can also be applied the other way around.

function onRecvPacket(packet: Packet): bytes {
  if (packet.data is RunTxPacketData) {
    const tx = deserialiseTx(packet.data.txBytes)
    abortTransactionUnless(authenticateTx(tx))
    return runTx(tx)
  }

  if (packet.data is RegisterIBCAccountPacketData) {
    RegisterIBCAccountPacketData data = packet.data
    identifier = "{packet/sourcePort}/{packet.sourceChannel}"
    const address = generateAddress(identifier, packet.salt)
    createAccount(address)
    // Return generated address.
    return address
  }

  return 0x
}
function onAcknowledgePacket(
  packet: Packet,
  acknowledgement: bytes) {
  if (packet.data is RegisterIBCAccountPacketData)
    if (acknowledgement !== 0x) {
      identifier = "{packet/sourcePort}/{packet.sourceChannel}"
      onAccountCreated(identifier, acknowledgement)
    }
  if (packet.data is RunTxPacketData) {
    identifier = "{packet/destPort}/{packet.destChannel}"
    if (acknowledgement === 0x)
        onTxSucceeded(identifier: Identifier, packet.data.txBytes)
    else
        onTxFailed(identifier: Identifier, packet.data.txBytes, acknowledgement)
  }
}
function onTimeoutPacket(packet: Packet) {
  // Receiving chain should handle this event as if the tx in packet has failed
  if (packet.data is RunTxPacketData) {
    identifier = "{packet/destPort}/{packet.destChannel}"
    // 0x99 error code means timeout.
    onTxFailed(identifier: Identifier, packet.data.txBytes, 0x99)
  }
}
function onTimeoutPacketClose(packet: Packet) {
  // nothing is necessary
}

Backwards Compatibility

Not applicable.

Forwards Compatibility

Not applicable.

Example Implementation

Pseudocode for cosmos-sdk: https://github.com/everett-protocol/everett-hackathon/tree/master/x/interchain-account POC for Interchain account on Ethereum: https://github.com/everett-protocol/ethereum-interchain-account

Other Implementations

(links to or descriptions of other implementations)

History

Aug 1, 2019 - Concept discussed

Sep 24, 2019 - Draft suggested

Nov 8, 2019 - Major revisions

Dec 2, 2019 - Minor revisions (Add more specific description & Add interchain account on Ethereum)

Copyright

All content herein is licensed under Apache 2.0.