Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/ibc: transfer specs #7580

Merged
merged 23 commits into from
Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a553c2e
x/ibc: transfer specs
fedekunze Oct 16, 2020
565a73f
update titles and list todos
fedekunze Oct 16, 2020
bac8d80
Merge branch 'master' of github.com:cosmos/cosmos-sdk into fedekunze/…
fedekunze Oct 27, 2020
2b4720e
denom trace and client UX
fedekunze Oct 27, 2020
45566dc
Merge branch 'master' into fedekunze/ibc-transfer-spec
fedekunze Oct 29, 2020
14a7f1f
add UX recommendations and locked funds section
fedekunze Oct 29, 2020
727197b
metrics
fedekunze Oct 29, 2020
df7af68
state transitions
fedekunze Oct 29, 2020
8d1edf4
Apply suggestions from code review
fedekunze Oct 29, 2020
abdc4ed
address comments from review
fedekunze Oct 29, 2020
e66a6c9
conflicts
fedekunze Oct 29, 2020
f35e0ad
jack suggestions
fedekunze Oct 29, 2020
31bc40e
add example
fedekunze Oct 29, 2020
334f37f
fix
fedekunze Oct 29, 2020
7d30b74
Merge branch 'master' into fedekunze/ibc-transfer-spec
fedekunze Nov 2, 2020
d74ca2a
address comments from call with @zakimanian
fedekunze Nov 2, 2020
dd67f1f
Merge branch 'fedekunze/ibc-transfer-spec' of github.com:cosmos/cosmo…
fedekunze Nov 2, 2020
2a3e677
Update x/ibc/applications/transfer/spec/01_concepts.md
fedekunze Nov 2, 2020
eb70a25
Merge branch 'master' into fedekunze/ibc-transfer-spec
fedekunze Nov 2, 2020
a349a93
add comment about verification
fedekunze Nov 2, 2020
2b8fb82
address comments from review
fedekunze Nov 4, 2020
742d728
Merge branch 'master' into fedekunze/ibc-transfer-spec
fedekunze Nov 4, 2020
20efdf8
Merge branch 'master' into fedekunze/ibc-transfer-spec
fedekunze Nov 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 84 additions & 2 deletions x/ibc/applications/transfer/spec/01_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,89 @@ order: 1
ICS20 uses the recommended acknowledgement format as specified by [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope).

A successful receive of a transfer packet will result in a Result Acknowledgement being written
with the value `[]byte(byte(1))` in the `Response` field.
with the value `[]byte(byte(1))` in the `Response` field.

An unsuccessful receive of a transfer packet will result in an Error Acknowledgement being written
with the error message in the `Response` field.
with the error message in the `Response` field.

## Denomination Trace

The denomination trace corresponds to the information that allows a token to be traced back to its
origin chain. It contains a sequence of port and channel identifiers ordered from the most recent to
the oldest in the timeline of transfers.

This information is included on the token denomination field in the form of a hash to prevent an
unbounded denomination length. For example, the token `transfer/channelToA/uatom` will be displayed
as `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`.

Each send to any chain other than the one it was previously received from is a movement forwards in
the token's timeline. This causes trace to be added to the token's history and the destination port
and destination channel to be prefixed to the denomination. In these instances the sender chain is
acting as the "source zone". When the token is sent back to the chain it previously received from, the
prefix is removed. This is a backwards movement in the token's timeline and the sender chain is
acting as the "sink zone".
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

It is strongly recommended to read the full details of [ADR 001: Coin Source Tracing](./../../../../../docs/architecture/adr-001-coin-source-tracing.md) to understand the implications and context of the IBC token representations.

### UX suggestions for clients

For clients that want to display the source of the token, it is recommended to use the following
alternatives for each of the following cases:

#### Direct connection
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#### Direct connection
#### Single Channel Hop

Connection is a name-clash with IBC connection which is separate


If the denomination trace contains a single identifier prefix pair (as in the example above), then
the easiest way to retrieve the chain and light client identifier is to map the trace information
directly. In summary, this requires querying the channel from the denomination trace identifiers,
and then the counterparty client state using the counterparty port and channel identifiers from the
retrieved channel.

A general pseudo algorithm would look like the following:

1. Query the full denomination trace.
2. Query the channel with the `portID/channelID` pair, which corresponds to the first destination of the
token.
3. Query the client state using the identifiers pair. Note that this query will return a `"Not
Found"` response if the current chain is not connected to this channel.
4. Retrieve the the client identifier or chain identifier from the client state (eg: on
Tendermint clients) and store it locally.
Comment on lines +52 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
2. Query the channel with the `portID/channelID` pair, which corresponds to the first destination of the
token.
3. Query the client state using the identifiers pair. Note that this query will return a `"Not
Found"` response if the current chain is not connected to this channel.
4. Retrieve the the client identifier or chain identifier from the client state (eg: on
Tendermint clients) and store it locally.
2. Query the client state associated with the `portID/channelID` channel, which corresponds to the source of the
token.
Note that this query will return a `"Not Found"` response if the current chain is not connected to this channel.
3. Retrieve the the client identifier or chain identifier from the client state (eg: on
Tendermint clients) and store it locally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to perform the query using the channel counterparty to get the origin chain


Using the gRPC gataway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Using the gRPC gataway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`:
Using the gRPC gataway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` stored on `chainB`:


1. `GET /ibc_transfer/v1beta1/denom_traces/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` -> `{"path": "transfer/channelToA", "base_denom": "uatom"}`
2. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer/client_state"` -> `{"client_id": "clientA", "chain-id": "chainA", ...}`
3. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer"` -> `{"channel_id": "channelToA", port_id": "transfer", counterparty: {"channel_id": "channelToB", port_id": "transfer"}, ...}`
4. `GET /ibc/channel/v1beta1/channels/channelToB/ports/transfer/client_state" -> {"client_id": "clientB", "chain-id": "chainB",}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks wrong, in a direct connection there would be no need to query the counterparty chainID information because we already know our own chain-id.

Suggested change
1. `GET /ibc_transfer/v1beta1/denom_traces/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` -> `{"path": "transfer/channelToA", "base_denom": "uatom"}`
2. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer/client_state"` -> `{"client_id": "clientA", "chain-id": "chainA", ...}`
3. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer"` -> `{"channel_id": "channelToA", port_id": "transfer", counterparty: {"channel_id": "channelToB", port_id": "transfer"}, ...}`
4. `GET /ibc/channel/v1beta1/channels/channelToB/ports/transfer/client_state" -> {"client_id": "clientB", "chain-id": "chainB",}`
1. `GET /ibc_transfer/v1beta1/denom_traces/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` -> `{"path": "transfer/channelToA", "base_denom": "uatom"}`
2. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer/client_state"` -> `{"client_id": "clientA", "chain-id": "chainA", ...}`


Then, the token transfer chain path for the `uatom` denomination would be: `chainB` -> `chainA`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Then, the token transfer chain path for the `uatom` denomination would be: `chainB` -> `chainA`.
Then, the token transfer chain path for the `uatom` denomination would be: `chainA` -> `chainB`.


### Multiple connection hops
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

The IBC protocol doesn't know the topology of the overall network (i.e connections between chains and identifier names between them). In the concrete case of fungible token transfers with more than one connection hop, a particular chain in the timeline of the individual transfers can't query the chain and client identifiers of the other chains.
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

Take for example the following sequence of transfers `A -> B -> C` for an IBC token, with a final prefix path (trace info) of `transfer/channelChainC/transfer/channelChainB`. What the paragraph above means is that is that even in the case that chain `C` is directly connected to chain `A`, querying the port and channel identifiers that chain `B` uses to connect to chain `A` (eg: `transfer/channelChainA`) can be completely different from the one that chain `C` uses to connect to chain `A` (eg: `transfer/channelToChainA`).

Thus the proposed solution for clients that the IBC team recommends are the following:

- **Connect to all chains**: Connecting to all the chains in the timeline would allow clients to
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
perform the queries outlined in the [direct connection](#direct-connection) section to each
relevant chain. By repeatedly following the port and channel denomination trace transfer timeline,
clients should always be able to find all the relevant identifiers. This comes at the tradeoff
that the client must connect to nodes on each of the chains in order to perform the queries.
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
- **Relayer as a Service (RaaS)**: A longer term solution is to use/create a relayer service that
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
could map the denomination trace to the chain path timeline for each token (i.e `origin chain ->
chain #1 -> ... -> chain #(n-1) -> final chain`). Clients would be advised to connect to public
relayers that support the largest number of connections between chains in the ecosystem. Unfortunately, none of the existing public relayers (in [Golang](https://github.com/cosmos/relayer) and [Rust](https://github.com/informalsystems/ibc-rs)), provide this service to clients.
Copy link
Member

@AdityaSripal AdityaSripal Nov 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessarily a relayer function (though relayers could also provide this service). This really is describing an IBC explorer which is a separate thing from a relayer service.

Suggested change
- **Relayer as a Service (RaaS)**: A longer term solution is to use/create a relayer service that
could map the denomination trace to the chain path timeline for each token (i.e `origin chain ->
chain #1 -> ... -> chain #(n-1) -> final chain`). Clients would be advised to connect to public
relayers that support the largest number of connections between chains in the ecosystem. Unfortunately, none of the existing public relayers (in [Golang](https://github.com/cosmos/relayer) and [Rust](https://github.com/informalsystems/ibc-rs)), provide this service to clients.
- **IBC Explorer**: Another solution is to use/create an IBC explorer service that maps the locally used channel and client identifiers used by each chain to the global identifiers (chain-id) that they correspond to. A client can then take a given denomination trace and iteratively query each channel identifier against the chain that uses that identifier in the token's timeline. This prevents clients from having to establish connections to every chain in the timeline, but it does introduce a trusted party in the IBC explorer.
Given the existence of an IBC explorer service. A client psuedo algorithm may look like this:
1. Retrieve the denomination trace of the token => `transfer/channelC/transfer/channelB/transfer/channelA` on the current chain `chainD`.
2. Retrieve the immediately preceding blockchain `chainC` in the timeline by querying the explorer against our current `chainD` with the first channel-id `channelC` in the trace.
3. Use the resulting `chainC` to query for the prior blockchain in the timeline by querying the explorer for the blockchain corresponding to `channelB` on `chainC`.
4. Use the resulting `chainB` to query for the prior blockchain in the timeline by querying the explorer for the blockchain corresponding to `channelA` on `chainB`.
This will provide a full history of the token: `chainA (source location)` -> `chainB` -> `chainC` -> `chainD (current location)`
**Important Note**: The only thing that is provable from `chainD` is the token trace which is a list of the local channel- and port-identifiers the token passed through from source to the current location. From this information, the chain-id of the immediate predecessor is also provable as shown in the `Single channel hop` example. IBC explorers must be trusted by clients to provide the correct chain history given a list of local channel identifiers. Since this is a relaxation of the security model of the core IBC spec, any chain history provided by an IBC explorer must be explicitly understood and displayed as coming from a (potentially) untrustworthy IBC explorer so that end users may decide whether or not to trust that information.
An implementation of an IBC explorer does not currently exist, though they may become an important part of the ecosystem as multi-chain token histories become more common.


::: tip
The only viable alternative for clients (at the time of writing) to tokens with multiple connection hops, is to connect to all chains directly and perform relevant queries to each of them in the sequence.
:::
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved

## Locked Funds
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to this, we should also mention that to retrieve a token in its original form, the token must be sent back along the exact route that it took originally. Sending a token back to the same chain across a different channel will not move the token back across its timeline. If a channel in the chain history closes before the token can be sent back across that channel, then the token will not be returnable to its original form.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really important point and something user interfaces should help with


In some [exceptional cases](./../../../../../docs/architecture/adr-026-ibc-client-recovery-mechanisms.md#exceptional-cases), a client state associated with a given channel cannot be updated. This causes that funds from fungible tokens in that channel will be permanently locked and thus can no longer be transferred.

To mitigate this, a client update governance proposal can be submitted to update the frozen client
with a new valid header. Once the proposal passes the client state will be unfrozen and the funds
from the associated channels will then be unlocked. This mechanism only applies to clients that
allow updates via governance, such as Tendermint clients.
5 changes: 5 additions & 0 deletions x/ibc/applications/transfer/spec/02_state.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ order: 2
-->

# State

The transfer IBC application module keeps state of the port to which the module is binded and the denomination trace information as outlined in [ADR 01](./../../../../../docs/architecture/adr-001-coin-source-tracing.md).

- `Port`: `0x01 -> ProtocolBuffer(string)`
- `DenomTrace`: `0x02 | []bytes(traceHash) -> ProtocolBuffer(DenomTrace)`
31 changes: 31 additions & 0 deletions x/ibc/applications/transfer/spec/03_state_transitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,34 @@ order: 3
-->

# State Transitions

## Send Fungible Tokens

A successful fungible token send has two state transitions depending if the
transfer is a movement forward or backwards in the token's timeline:

1. Sender chain is the source chain, *i.e* a transfer to any chain other than the one it was previously received from is a movement forwards in the token's timeline. This results in the following state transitions:

- The coins are transferred to an escrow address (i.e locked) on the sender chain
- The coins are transferred to the receiving chain through IBC TAO logic.

2. Sender chain is the sink chain, *i.e* the token is sent back to the chain it previously received from. This is a backwards movement in the token's timeline. This results in the following state transitions:

- The coins (vouchers) are burned on the sender chain
- The coins transferred to the receiving chain though IBC TAO logic.

## Receive Fungible Tokens

A successful fungible token receive has two state transitions depending if the
transfer is a movement forward or backwards in the token's timeline:

1. Receiver chain is the source chain. This is a backwards movement in the token's timeline. This results in the following state transitions:

- The leftmost port and channel identifier pair is removed from the token denomination prefix.
- The tokens are unescrowed and sent to the receiving address.

2. Receiver chain is the sink chain. This is a movement forwards in the token's timeline. This results in the following state transitions:

- Token vouchers are minted by prefixing the destination port and channel identifiers to the trace information.
- The receiving chain stores the new trace information in the store (if not set already).
- The vouchers are sent to the receiving address.
4 changes: 2 additions & 2 deletions x/ibc/applications/transfer/spec/04_messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type MsgTransfer struct {
SourcePort string
SourceChannel string
Token sdk.Coin
Sender sdk.AccAddress
Sender string
Receiver string
TimeoutHeight ibcexported.Height
TimeoutTimestamp uint64
Expand All @@ -29,7 +29,7 @@ This message is expected to fail if:
- `Sender` is empty
- `Receiver` is empty
- `TimeoutHeight` and `TimeoutTimestamp` are both zero
- `Token.Denom` is not a valid IBC denomination as per [ADR 001 - Coin Source Tracing](./../../../docs/architecture/adr-001-coin-source-tracing.md).
- `Token.Denom` is not a valid IBC denomination as per [ADR 001 - Coin Source Tracing](./../../../../../docs/architecture/adr-001-coin-source-tracing.md).

This message will send a fungible token to the counterparty chain represented
by the counterparty Channel End connected to the Channel End with the identifiers
Expand Down
14 changes: 14 additions & 0 deletions x/ibc/applications/transfer/spec/06_metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!--
order: 6
-->

# Metrics

The transfer IBC application module exposes the following set of [metrics](./../../../../../docs/core/telemetry.md).

| Metric | Description | Unit | Type |
|:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------|
| `tx_msg_ibc_transfer` | The total amount of tokens transferred via IBC in a `MsgTransfer` (source or sink chain) | token | gauge |
| `ibc_transfer_packet_receive` | The total amount of tokens received in a `FungibleTokenPacketData` (source or sink chain) | token | gauge |
| `ibc_transfer_send` | Total number of IBC transfers sent from a chain (source or sink) | transfer | counter |
| `ibc_transfer_receive` | Total number of IBC transfers received to a chain (source or sink) | transfer | counter |
Comment on lines +11 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tagged with src/dst chan/port and other metadata I would assume?

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
order: 6
order: 7
-->

# Parameters
Expand All @@ -17,7 +17,7 @@ The transfers enabled parameter controls send cross-chain transfer capabilities
tokens.

To prevent a single token from being transferred from the chain, set the `SendEnabled` parameter to `true` and
then set the bank module's [`SendEnabled` parameter](./../../bank/spec/05_params.md#sendenabled) for
then set the bank module's [`SendEnabled` parameter](./../../../../bank/spec/05_params.md#sendenabled) for
the denomination to `false`.

## ReceiveEnabled
Expand All @@ -26,5 +26,5 @@ The transfers enabled parameter controls receive cross-chain transfer capabiliti
tokens.

To prevent a single token from being transferred to the chain, set the `ReceiveEnabled` parameter to `true` and
then set the bank module's [`SendEnabled` parameter](./../../bank/spec/05_params.md#sendenabled) for
then set the bank module's [`SendEnabled` parameter](./../../../../bank/spec/05_params.md#sendenabled) for
the denomination to `false`.
3 changes: 2 additions & 1 deletion x/ibc/applications/transfer/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ For the general specification please refer to the [ICS20 Specification](https://
3. **[State Transitions](03_state_transitions.md)**
4. **[Messages](04_messages.md)**
5. **[Events](05_events.md)**
6. **[Parameters](06_params.md)**
6. **[Metrics](06_metrics.md)**
7. **[Parameters](07_params.md)**