From 6fb05ff210b20ca6c47a9b5e981d44271cf1aaba Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 10 Aug 2022 16:08:53 +0200 Subject: [PATCH] update for multitoken --- .../dev/src/explore/design/ledger/ibc.md | 18 ++++--- .../specs/src/interoperability/ibc.md | 53 +++++++++++++++---- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/documentation/dev/src/explore/design/ledger/ibc.md b/documentation/dev/src/explore/design/ledger/ibc.md index 75a5aa17c6..6129d9fc8c 100644 --- a/documentation/dev/src/explore/design/ledger/ibc.md +++ b/documentation/dev/src/explore/design/ledger/ibc.md @@ -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") diff --git a/documentation/specs/src/interoperability/ibc.md b/documentation/specs/src/interoperability/ibc.md index cf9610c054..201a5e7b18 100644 --- a/documentation/specs/src/interoperability/ibc.md +++ b/documentation/specs/src/interoperability/ibc.md @@ -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