Skip to content

Commit

Permalink
Merge PR cosmos#278: ICS 027: Interchain account [draft]
Browse files Browse the repository at this point in the history
  • Loading branch information
Thunnini authored and cwgoes committed Dec 2, 2019
1 parent 979dacc commit a95b4fd
Showing 1 changed file with 298 additions and 0 deletions.
298 changes: 298 additions & 0 deletions spec/ics-027-interchain-account/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
---
ics: 27
title: Interchain Account
stage: Draft
category: IBC/TAO
requires: 25, 26
kind: instantiation
author: Tony Yun <[email protected]>, Dogemos <[email protected]>
created: 2019-08-01
modified: 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](https://github.com/ethereum/wiki/wiki/White-Paper)]. 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](https://github.com/cosmos/ics/tree/master/spec/ics-025-handler-interface) and [ICS 26](https://github.com/cosmos/ics/tree/master/spec/ics-026-routing-module), respectively.

### Desired Properties

- Permissionless
- Fault containment: Interchain account must follow rules of its host chain, even in times of Byzantine behavior 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 deserialize 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 serialize/deserialize transaction bytes in order to send transactions via IBC. And the counterparty chain must implement the process of safely exececuting 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 deserialized.
- 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 serialized 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.

```typescript
type Tx = obejct
interface IBCAccountModule {
createOutgoingPacket(chainType: Uint8Array, data: any)
createAccount(address: Uint8Array)
generateAddress(identifier: Identifier, salt: Uint8Array): Uint8Array
deserializeTx(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 utilizing identifier and salt.

```typescript
interface RegisterIBCAccountPacketData {
salt: Uint8Array
}
```

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

```typescript
interface RunTxPacketData {
txBytes: Uint8Array
}
```

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

```typescript
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 initialized) to bind to the appropriate port and create an escrow address (owned by the module).

```typescript
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.

```typescript
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 === "")
}
```

```typescript
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")
}
```

```typescript
function onChanOpenAck(
portIdentifier: Identifier,
channelIdentifier: Identifier,
version: string) {
// version not used at present
abortTransactionUnless(version === "")
// port has already been validated
}
```

```typescript
function onChanOpenConfirm(
portIdentifier: Identifier,
channelIdentifier: Identifier) {
// accept channel confirmations, port has already been validated
}
```

```typescript
function onChanCloseInit(
portIdentifier: Identifier,
channelIdentifier: Identifier) {
// no action necessary
}
```

```typescript
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.

```typescript
function onRecvPacket(packet: Packet): bytes {
if (packet.data is RunTxPacketData) {
const tx = deserializeTx(packet.data.txBytes)
abortTransactionUnless(authenticateTx(tx) == true)
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
}
```

```typescript
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)
}
}
}
```

```typescript
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)
}
}
```

```typescript
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

01 August 2019 - Concept discussed
24 September 2019 - Draft suggested
8 November 2019 - Major revisions
2 December 2019 - Minor revisions (Add more specific description & Add interchain account on Ethereum)

## Copyright

All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).

0 comments on commit a95b4fd

Please sign in to comment.