-
Notifications
You must be signed in to change notification settings - Fork 384
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
Specify ics20-2 #577
Specify ics20-2 #577
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ The IBC handler interface & IBC routing module interface are as defined in [ICS | |
- Permissionless token transfers, no need to whitelist connections, modules, or denominations. | ||
- Symmetric (all chains implement the same logic, no in-protocol differentiation of hubs & zones). | ||
- Fault containment: prevents Byzantine-inflation of tokens originating on chain `A`, as a result of chain `B`'s Byzantine behaviour (though any users who sent tokens to chain `B` may be at risk). | ||
- Opt-in, decentralized, pseudonymous relayer incentivization ([See ICS20-2](#Extension-ICS20-2)) | ||
|
||
## Technical Specification | ||
|
||
|
@@ -330,13 +331,146 @@ In order to track all of the denominations moving around the network of chains i | |
- Each chain, locally, could elect to keep a lookup table to use short, user-friendly local denominations in state which are translated to and from the longer denominations when sending and receiving packets. | ||
- Additional restrictions may be imposed on which other machines may be connected to & which channels may be established. | ||
|
||
## Extension: ICS20-2 | ||
|
||
The above specifications defines `ics20-1`, which is the first version of the specification. Here we define | ||
ICS20-2 which | ||
|
||
### Goals | ||
|
||
- Add support for opt-in, decentralized, pseudonymous relayer incentivization | ||
- Backwards compatibility with ics20-1 | ||
- Minimal extra logic needed to maintain backwards compatibility | ||
- Fault-resilient: version implementation errors should not break invariants | ||
|
||
### Data Structures | ||
|
||
In order to help with backwards compatibility, we define a packet structure for ICS20-2, which is a superset of ICS20-1, such that any valid packet for one protocol, is also a valid packet for the other protocol. The only issue is that the `fee` info from a ICS20-2 packet will be ignored by a ICS20-1 handler. | ||
|
||
```typescript | ||
interface FungibleTokenPacketData { | ||
denomination: string | ||
amount: uint256 | ||
sender: string | ||
receiver: string | ||
fee: Maybe<uint256> | ||
} | ||
``` | ||
|
||
Fee is defined here as some number of tokens to be sent to the address who submitted the IbcReceivePacket on the destination chain. The information of `signer` is available when submitting the packet, but discarded in the application-specific handlers for `ibc-go`. We can simply expose that information to the application to allow it to optionally take action based on who submitted the packet. Note that this means *anyone* can submit a valid packet and we do not hard code an allowed relayer nor force the token sender to select a relayer. | ||
|
||
In order to maintain maximum compatibility with `ics20-1`, we will define `amount` and `fee` in such a way that a valid `ics20-2` sender and a valid `ics20-1` recipient will not create or destroy any tokens (just ignore the fee field when set). That means: | ||
|
||
- `amount` is the total amount that is escrowed by the sending chain. | ||
- `amount - (fee || 0)` is the total amount to be sent to the `receiver` on the receiving chain | ||
- `fee || 0` is the total amount to be sent to the relayer account (`signer`) | ||
Comment on lines
+362
to
+366
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really think that we should allow fee to be payed in a different denomination. Since ICS-20 currently only supports a single coin amount, we would need to change the logic here such that Even with the existence of AMM, I don't expect relayers to be perfectly agnostic as to what denomination they get paid in. I still expect that they will want to be paid in a very liquid, high market-cap token. The proposal as is would work fine for ICS-20 transfer packets sending well-known desirable tokens. However, packets sending low-liquidity, low-market-cap tokens will be greatly disadvantaged. They will either have to pay an exorbitant fee or their packets may not get relayed at all. Suppose I create From a user perspective, I want my packets relayed and treated the same regardless of what denomination my Thus, I think having a different denom fee is a must have even for MVP and especially because it will be used in a DEX where denominations with wildly different liquidities/desirability/market-cap are going to be sent around and they all need to be relayed just the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting point |
||
|
||
Defining `amount + fee` to be escrowed on the sending chain would potentially cause lost tokens if `fee` were set in the packet sender, but the recipient ignored the field. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is paid on the receiving (destination) chain, the same time Or do I misunderstand your comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, however this is fixable if all of the fee payout gets processed on the sender chain correct? In the case where the acknowledgement does not contain |
||
|
||
### Handshakes | ||
|
||
Handshakes work much like `ics20-1` with some extensions. | ||
|
||
Both machines `A` and `B` accept new channels from any module on another machine, if and only if: | ||
|
||
- The channel being created is unordered. | ||
- The version string is `ics20-1` OR `ics20-2` | ||
- Both version and counterparty_version string are the same | ||
|
||
The handshake implementation looks much like the above definition with the additional acceptance of `ics20-2` version string. Note that `ics20-2` implementations should store the channel version data to be used when creating packets to send (whether or not fee field is supported). | ||
|
||
### Implementation | ||
|
||
This is very similar to the `ics20-1` implementation, except for a few additions about fee handling. Note that `createOutgoingPacket` now needs both the extra `fee` information (set by the blockchain user), as well as the channel `version` (stored when the channel was created): | ||
|
||
```typescript | ||
function createOutgoingPacket( | ||
denomination: string, | ||
amount: uint256, | ||
sender: string, | ||
receiver: string, | ||
source: boolean, | ||
fee: Maybe<uint256>, | ||
destPort: string, | ||
destChannel: string, | ||
sourcePort: string, | ||
sourceChannel: string, | ||
version: string, | ||
timeoutHeight: Height, | ||
timeoutTimestamp: uint64) { | ||
prefix = "{sourcePort}/{sourceChannel}/" | ||
// we are the source if the denomination is not prefixed | ||
source = denomination.slice(0, len(prefix)) !== prefix | ||
if source { | ||
// determine escrow account | ||
escrowAccount = channelEscrowAddresses[sourceChannel] | ||
// escrow source tokens (assumed to fail if balance insufficient) | ||
bank.TransferCoins(sender, escrowAccount, denomination, amount) | ||
} else { | ||
// receiver is source chain, burn vouchers | ||
bank.BurnCoins(sender, denomination, amount) | ||
} | ||
// ADDED: we never set this field when talking with an ics20-1 chain | ||
if version == "ics20-1" | ||
fee = None | ||
FungibleTokenPacketData data = FungibleTokenPacketData{denomination, amount, sender, receiver, fee} | ||
handler.sendPacket(Packet{timeoutHeight, timeoutTimestamp, destPort, destChannel, sourcePort, sourceChannel, data}, getCapability("port")) | ||
} | ||
``` | ||
|
||
Likewise, `onRecvPacket` must distribute the fee (if set). This requires passing in the message signer field into the application: | ||
|
||
```typescript | ||
function onRecvPacket(packet: Packet, singer: string) { | ||
FungibleTokenPacketData data = packet.data | ||
// construct default acknowledgement of success | ||
FungibleTokenPacketAcknowledgement ack = FungibleTokenPacketAcknowledgement{true, null} | ||
prefix = "{packet.sourcePort}/{packet.sourceChannel}/" | ||
// we are the source if the packets were prefixed by the sending chain | ||
source = data.denomination.slice(0, len(prefix)) === prefix | ||
// ADDED: calculate recipient amount | ||
toRcpt = data.amount - (data.fee || 0) | ||
if source { | ||
// receiver is source chain: unescrow tokens | ||
// determine escrow account | ||
escrowAccount = channelEscrowAddresses[packet.destChannel] | ||
// unescrow tokens to receiver (assumed to fail if balance insufficient) | ||
err = bank.TransferCoins(escrowAccount, data.receiver, data.denomination.slice(len(prefix)), toRcpt) | ||
if (err !== nil) | ||
ack = FungibleTokenPacketAcknowledgement{false, "transfer coins failed"} | ||
// ADDED: handle fee distribution | ||
if data.fee { | ||
err = bank.TransferCoins(escrowAccount, signer, data.denomination.slice(len(prefix)), data.fee) | ||
if (err !== nil) | ||
ack = FungibleTokenPacketAcknowledgement{false, "transfer coins failed"} | ||
} | ||
} else { | ||
prefix = "{packet.destPort}/{packet.destChannel}/" | ||
prefixedDenomination = prefix + data.denomination | ||
// sender was source, mint vouchers to receiver (assumed to fail if balance insufficient) | ||
err = bank.MintCoins(data.receiver, prefixedDenomination, toRcpt) | ||
if (err !== nil) | ||
ack = FungibleTokenPacketAcknowledgement{false, "mint coins failed"} | ||
// ADDED: handle fee distribution | ||
if data.fee { | ||
err = bank.MintCoins(signer, prefixedDenomination, data.fee) | ||
if (err !== nil) | ||
ack = FungibleTokenPacketAcknowledgement{false, "mint coins failed"} | ||
} | ||
} | ||
return ack | ||
} | ||
``` | ||
|
||
The rest of the logic can remain unchanged. Note that the biggest changes are simply providing the additional information to the packet handlers - channel version to the sending logic, and message signer to the receive logic. Both the acknowledgement and timeout handlers can remain unchanged, as `amount` remains the total amount locked up in the sending chain. | ||
|
||
## Backwards Compatibility | ||
|
||
Not applicable. | ||
All `ics20-1` packets are valid `ics20-2` packets. An `ics20-2` packet received by an `ics20-1` handler may be incorrectly processed (no funds sent to the relayer), but will not break any invariants (total number of tokens escrowed on sender == total number of tokens issued on receiver). | ||
|
||
## Forwards Compatibility | ||
|
||
This initial standard uses version "ics20-1" in the channel handshake. | ||
This initial standard uses version "ics20-1" in the channel handshake. We also define a backwards-compatible "ics20-2" extension with minimal changes to the handlers. | ||
|
||
A future version of this standard could use a different version in the channel handshake, | ||
and safely alter the packet data format & packet handler semantics. | ||
|
@@ -363,6 +497,8 @@ Feb 24, 2020 - Revisions to infer source field, inclusion of version string | |
|
||
July 27, 2020 - Re-addition of source field | ||
|
||
May 24, 2020 - Added ICS20-2 extension | ||
|
||
## Copyright | ||
|
||
All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). |
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.
Do we want to consider a relayer address to be optionally provided by the message sender to support permissioned relaying? If field is left empty (default) anyone can relay.
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.
Let us discuss this. I believe there was a great concern that permissioned relaying would involve money transfer laws and require heavy regulation. We should wait until the lawyers come back with their analysis of the law, but I would add a comment on this in any case.