Skip to content

Commit

Permalink
update for multitoken
Browse files Browse the repository at this point in the history
  • Loading branch information
yito88 committed Jan 5, 2023
1 parent cf2fc1d commit 6fb05ff
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 16 deletions.
18 changes: 11 additions & 7 deletions documentation/dev/src/explore/design/ledger/ibc.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,25 +533,29 @@ IBC token VP as a native VP should check if the escrow/unescrow/burn/mint has be
In a transaction with `MsgTransfer` (defined in ibc-rs) including `FungibleTokenPacketData` as transaction data, the specified token is sent according to ICS20 and a packet is sent.
The transaction updates the sender's balance by escrowing or burning the amount of the token. The account, the sent token(denomination), and the amount are specified by `MsgTransfer`. [The denomination field would indicate that this chain is the source zone or the sink zone](https://github.com/cosmos/ibc/blob/master/spec/app/ics-020-fungible-token-transfer/README.md#technical-specification).

#### Sender
Basically, the sender key is `{token_addr}/balance/{sender_addr}`. `{token_addr}` and `{sender_addr}` is specified by `FungibleTokenPacketData`. When the denomination `{denom}` in `FungibleTokenPacketData` specifies the source chain, the transfer operation is executed from the origin-specific account `{token_addr}/ibc/{ibc_token_hash}/balance/{sender_addr}` (Ref. [Receiver](#Receiver)). We can set `{token_addr}`, `{port_id}/{channel_id}/../{token_addr}`, or `ibc/{ibc_token_hash}/{token_addr}` to the denomination. When `ibc/{ibc_token_hash}/` is prefixed, the transfer looks up the prefixed denomination `{port_id}/{channel_id}/{denom}` with the `{ibc_token_hash}`. `{denom}` might have other prefixes to specify the source chains, e.g. `{port_id_b}/{channel_id_b}/{port_id_a}/{channel_id_a}/{token_addr}`. Accoding to the prefixed port ID and channel ID, the transfer operation escrows or burns the amount of the token (ICS20).

#### Escrow
When this chain is the source zone, the amount of the specified token is sent from the sender's account key (e.g. `{token_addr}/balance/{sender_addr}`) to the escrow key `{token_addr}/{ibc_token_addr}/balance/{escrow_addr}`. The `ibc_token_addr` is calculated with the token address, the IBC port ID and the channel ID. The escrow address should be associated with IBC port ID and channel ID to unescrow it later. The escrow address is one of internal addresses, `InternalAddress::Escrow` with the hash calculated with the port ID and the channel ID. It is not allowed to transfer from this account without IBC token transfer operation. IBC token VP should check the transfer from the escrow accounts.
When this chain is the source zone, i.e. the denomination does NOT start with the port ID and the channel ID of this chain, the amount of the specified token is sent from the sender's account key to the escrow key `{token_addr}/ibc/{port_id}/{channel_id}/balance/ESCROW_ADDR`. The escrow address should be associated with IBC port ID and channel ID to unescrow it later. The escrow address is one of internal addresses, `InternalAddress::Escrow`. It is not allowed to transfer from the escrow account without IBC token transfer operation. IBC token VP should check the transfer from the escrow accounts.

#### Burn
When this chain is the sink zone, the amount of the specified token is sent from the sender's account to a special key `{token_addr}/{ibc_token_addr}/balance/BURN_ADDR.` The `BURN_ADDR` is one of internal addresses, `InternalAddress::Burn`. The value of the key should NOT written to the block when the block is committed, i.e. reading the previous value of the key in a VP results in always `None` and the amount is zero by default. We can use `tx_write_temp` in the transaction for these writes.
When the destination chain was the source, i.e. the denomination starts with the port ID and the channel ID of this chain, the amount of the specified token is sent from the sender's account to a key `{token_addr}/ibc/{port_id}/{channel_id}/balance/BURN_ADDR`. `BURN_ADDR` is one of internal addresses, `InternalAddress::Burn`. The value of the key should NOT written to the block when the block is committed, i.e. reading the previous value of the key in a VP results in always `None` and the balance is zero by default. We can use `tx_write_temp` in the transaction for these writes.

### Receive a token
In a transaction with `MsgRecvPacket` (defined in ibc-rs) including `FungibleTokenTransferData` as transaction data, the specified token is received according to ICS20.
The transaction updates the receiver's balance by unescrowing or minting the amount of the token. The account(receiver), the received token(denomination), and the amount are specified by `FungibleTokenPacketData` in the received packet.

#### Receiver
The receiver's account key should be origin-specific because the token should be returned to the source chain if needed. The key is `{token_addr}/ibc/{ibc_token_hash}/balance/{receiver_addr}`. `{ibc_token_hash}` is calculated from the denomination prefixed with this chain's port ID and chain ID, and the token address. And, the original denomination should be persistent with the storage key `IBC_ADDR/denom/{ibc_token_hash}` for looking it up when sending the received token.

#### Unescrow
When this chain was the source zone, the amount of the token is sent from its escrow key `{token_addr}/{ibc_token_addr}/balance/{escrow_addr}` to the receiver's account key `{token_addr}/balance/{receiver_addr}`.
When this chain was the source zone, i.e. the denomination starts with this chain's port ID and channel ID, the amount of the token is sent from its escrow key `{token_addr}/ibc/{port_id}/{channel_id}/balance/ESCROW_ADDR`.

#### Mint
When this chain is not the source zone, the amount of the token is minted for the receiver's account `{token_addr}/{ibc_token_addr}/balance/{receiver_addr}`. The `ibc_token_addr` is calculated with the token address, the IBC port ID and the channel ID. It means that the token is originated from the source chain. Unlike an escrow account, the token can be transferred from the received account to another account on the chain without IBC operations.

The transaction should transfer the amount from the special account `{token_addr}/{ibc_token_addr}/balance/MINT_ADDR` to the receiver account. The `MINT_ADDR` is one of internal addresses, `InternalAddress::Mint`. The account is NOT updated when the block is committed same as `{token_addr}/{ibc_token_addr}/balance/BURN_ADDR`, i.e. reading the previous value of `{token_addr}/{ibc_token_addr}/balance/MINT_ADDR` in a VP results in always the maximum amount.
When this chain is not the source zone, i.e. the denomination does NOT start with this chain's port ID and channel ID, the amount of the token is minted from the mint account `{token_addr}/ibc/{port_id}/{channel_id}/balance/MINT_ADDR`. The `MINT_ADDR` is one of internal addresses, `InternalAddress::Mint`. The account is NOT updated when the block is committed same as `BURN_ADDR`, i.e. reading the previous value of the mint account in a VP results in always the maximum amount.

### Refund tokens
When a packet has timed out or a failure acknowledgement is given, the escrowed or burned amount of the token should be refunded by unescrowing or minting the amount of the token on the chain which has sent the token. i.e. the IBC transaction should transfer the amount of the token from `{token_addr}/{ibc_token_addr}/balance/{escrow_addr}` or `{token_addr}/{ibc_token_addr}/balance/MINT_ADDR` to the sender account.
When a packet has timed out or a failure acknowledgement is given, the escrowed or burned amount of the token should be refunded by unescrowing or minting the amount of the token on the chain which has sent the token. i.e. the IBC transaction should transfer the amount of the token from `{token_addr}/ibc/{port_id}/{channel_id}/balance/ESCROW_ADDR` or `{token_addr}/ibc/{port_id}/{channel_id}/balance/MINT_ADDR` to the sender account.

![transfer](./ibc/transfer.svg "transfer")
53 changes: 44 additions & 9 deletions documentation/specs/src/interoperability/ibc.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,50 @@ In Namada, the sending tokens is triggered by a transaction having [MsgTransfer]

Namada chain receives the tokens by a transaction having [MsgRecvPacket](https://github.com/informalsystems/ibc-rs/blob/0a952b295dbcf67bcabb79ce57ce92c9c8d7e5c6/modules/src/core/ics04_channel/msgs/recv_packet.rs#L19-L23) which has the packet including `FungibleTokenPacketData`.

The sending and receiving tokens in a transaction are validated by not only
IBC validity predicate but also [IBC token validity predicate](https://github.com/anoma/namada/blob/50b5e77f04a9afc036656353335bd232fcdba8a7/shared/src/ledger/ibc/vp/token.rs). IBC validity predicate validates if sending and receiving the packet is proper. IBC token validity predicate is also one of the native validity predicates and checks if the token transfer is valid. If the transfer is not valid, e.g. an unexpected amount is minted, the validity predicate makes the transaction fail.

A transaction escrowing/unescrowing a token changes the escrow account's
balance of the token. The key is `{token_addr}/balance/{escrow_addr}`. A
transaction burning a token changes the burn account's balance of the token.
The key is `{token_addr}/balance/BURN_ADDR`. A transaction minting a token
changes the mint account's balance of the token. The key is `{token_addr}
/balance/MINT_ADDR`. `{escrow_addr}`, `{BURN_ADDR}`, and `{MINT_ADDR}` are addresses of [`InternalAddress`](https://github.com/anoma/namada/blob/50b5e77f04a9afc036656353335bd232fcdba8a7/shared/src/types/address.rs). When these addresses are included in the changed keys after transaction execution, IBC token validity predicate is executed.
The sending and receiving tokens in a transaction are validated by not only IBC validity predicate but also [IBC token validity predicate](https://github.com/anoma/namada/blob/50b5e77f04a9afc036656353335bd232fcdba8a7/shared/src/ledger/ibc/vp/token.rs). IBC validity predicate validates if sending and receiving the packet is proper. IBC token validity predicate is also one of the native validity predicates and checks if the token transfer is valid. If the transfer is not valid, e.g. an unexpected amount is minted, the validity predicate makes the transaction fail.

A transaction escrowing/unescrowing a token changes the escrow account's balance of the token. The key is `{token_addr}/ibc/{port_id}/{channel_id}/balance/ESCROW_ADDR`. A transaction burning a token changes the burn account's balance of the token. The key is `{token_addr}/ibc/{port_id}/{channel_id}/balance/BURN_ADDR`. A transaction minting a token changes the mint account's balance of the token. The key is `{token_addr}/ibc/{port_id}/{channel_id}/balance/MINT_ADDR`. `ESCROW_ADDR`, `BURN_ADDR`, and `MINT_ADDR` are addresses of [`InternalAddress`](https://github.com/anoma/namada/blob/50b5e77f04a9afc036656353335bd232fcdba8a7/shared/src/types/address.rs). When these addresses are included in the changed keys after transaction execution, IBC token validity predicate is executed.

The receiver's account is `{token_addr}/ibc/{ibc_token_hash}/balance/{receiver_addr}`. `{ibc_token_hash}` is a hash calculated with the denomination prefixed with the port ID and channel ID. It is NOT the same as the normal account `{token_addr}/balance/{receiver_addr}`. That's because it should be origin-specific for transfer back to the source chain. We can transfer back the received token by prefixing the denomination with `{ibc_token_hash}/`, or `{port_id}/{channel_id}/`.

For example, we transfer a token `#my_token` from a user `#user_a` on Chain A to a user `#user_b` on Chain B, then transfer back the token from `#user_b` to `#user_a`. The port ID and channel ID on Chain A for Chain B are `transfer` and `channel_42`, those on Chain B for Chain A are `transfer` and `channel_24`. The denomination in the `FungibleTokenTransferData` at the first transfer should be `#my_token`.
1. User A makes `MsgTransfer` as a transaction data and submits a transaction from Chain A
```rust
let token = Some(Coin {
denom, // #my_token
amount: "100000".to_string(),
});
let msg = MsgTransfer {
source_port, // transfer
source_channel, // channel_42
token,
sender, // #user_a
receiver, // #user_b
timeout_height: Height::new(0, 1000),
timeout_timestamp: (Timestamp::now() + Duration::new(100, 0)).unwrap(),
};
```
1. On Chain A, the specified amount of the token is transferred from the sender's account `#my_token/balance/#user_a` to the escrow account `#my_token/ibc/transfer/channel_42/balance/ESCROW_ADDR`
1. On Chain B, the amount of the token is transferred from `#my_token/ibc/transfer/channel_24/balance/MINT_ADDR` to `#my_token/ibc/{hash}/balance/#user_b`
- The `{hash}` is calculated from a string `transfer/channel_24/#my_token` with SHA256
1. To transfer back, User B makes `MsgTransfer` and submits a transaction from Chain B
```rust
let token = Some(Coin {
denom, // ibc/{hash}/#my_token or transfer/channel_24/#my_token
amount: "100000".to_string(),
});
let msg = MsgTransfer {
source_port, // transfer
source_channel, // channel_24
token,
sender, // #user_b
receiver, // #user_a
timeout_height: Height::new(0, 1000),
timeout_timestamp: (Timestamp::now() + Duration::new(100, 0)).unwrap(),
};
```
1. On Chain B, the amount of the token is transferred from `#my_token/ibc/{hash}/balance/#user_b` to `#my_token/ibc/transfer/channel_24/BURN_ADDR`
1. On Chain A, the amount of the token is transferred from `#my_token/ibc/transfer/channel_42/balance/ESCROW_ADDR` to `#my_token/balance/#user_a`

## IBC message

Expand Down

0 comments on commit 6fb05ff

Please sign in to comment.