forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR cosmos#278: ICS 027: Interchain account [draft]
- Loading branch information
Showing
1 changed file
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |