-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
x/ibc: transfer specs #7580
Conversation
Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
…s-sdk into fedekunze/ibc-transfer-spec
Co-authored-by: Christopher Goes <cwgoes@pluranimity.org>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK though I still don't quite see how the proposed relayer service would work (in a verifiable fashion).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe connecting to all chains
is a workable solution for multi-hop tokens. Client-trusted IBC explorers seem like the only viable approach to me.
EDIT: See comment below, retracted
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",}` |
There was a problem hiding this comment.
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.
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", ...}` |
4. Retrieve the the client identifier or chain identifier from the client state (eg: on | ||
Tendermint clients) and store it locally. | ||
|
||
Using the gRPC gataway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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`: |
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
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
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",}` | ||
|
||
Then, the token transfer chain path for the `uatom` denomination would be: `chainB` -> `chainA`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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`. |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#### Direct connection | |
#### Single Channel Hop |
Connection is a name-clash with IBC connection which is separate
- **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. |
There was a problem hiding this comment.
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.
- **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. |
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. | ||
::: | ||
|
||
## Locked Funds |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
Misunderstood the |
One very important thing to note in the context of transfer security is that chain-ids (or denominations) are not globally unique. I can very easily create my own blockchain called This is IBC working exactly as intended, but leaves users vulnerable to potentially catastrophic mistakes imo. Perhaps the IBC team should suggest a way to mitigate this potential attack as well in this doc. |
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", ...}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a route that does this instead of pushing this work on to clients
Additionally, client would be advised in the future to use RaaS that support the largest number of | ||
connections between chains in the ecosystem. Unfortunately, none of the existing public relayers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding support for this in the upcoming relayer rest interface would be doable.
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. | ||
::: | ||
|
||
## Locked Funds |
There was a problem hiding this comment.
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
| `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 | |
There was a problem hiding this comment.
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?
Relevant to this topic is ongoing conversation at the Chain Agnostic Improvement Proposal repo: |
I agree with @jackzampolin that we should create a route that allows clients to get the client and chain data from a given IBC denomination. Although it is out-of-scope for this spec and should be done in a separate PR. |
No description provided.