ADR 001: coin cross-chain transfer source tracing (#6662)
* adr: coin cross-chain transfer source tracing * update pros and cons * update spec README * Update docs/architecture/adr-001-coin-source-tracing.md Co-authored-by: billy rennekamp <billy.rennekamp@gmail.com> * Apply suggestions from code review Co-authored-by: billy rennekamp <billy.rennekamp@gmail.com> * Update docs/architecture/adr-001-coin-source-tracing.md * address comments from review * update ADR with Send/Recv logic * final touches * Apply suggestions from code review Co-authored-by: Christopher Goes <cwgoes@pluranimity.org> * address comments from review * address @aaronc review comments * Apply suggestions from code review Co-authored-by: colin axner <25233464+colin-axner@users.noreply.github.com> * use SplitN * custom denom validation reference * address some comments from review * more updates based on Colin's review * final draft with changes to relay.go * undo proto changes * address @aaronc review comments * why do I keep updating the proto files? * address @AdityaSripal comments * address more comments * typos * final ammendments * minor fix * address more comments * update example * Update docs/architecture/adr-001-coin-source-tracing.md Co-authored-by: Anil Kumar Kammari <anil@vitwit.com> * address more comments * update prefix example Co-authored-by: billy rennekamp <billy.rennekamp@gmail.com> Co-authored-by: Christopher Goes <cwgoes@pluranimity.org> Co-authored-by: colin axner <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Anil Kumar Kammari <anil@vitwit.com>
This commit is contained in:
parent
5f5bdcbadf
commit
6f3ca5c140
|
@ -31,6 +31,7 @@ Please add a entry below in your Pull Request for an ADR.
|
||||||
|
|
||||||
## ADR Table of Contents
|
## ADR Table of Contents
|
||||||
|
|
||||||
|
- [ADR 001: Coin Source Tracing](./adr-001-coin-source-tracing.md)
|
||||||
- [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md)
|
- [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md)
|
||||||
- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md)
|
- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md)
|
||||||
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
||||||
|
@ -50,3 +51,4 @@ Please add a entry below in your Pull Request for an ADR.
|
||||||
- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md)
|
- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md)
|
||||||
- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md)
|
- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md)
|
||||||
- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md)
|
- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md)
|
||||||
|
- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md)
|
||||||
|
|
|
@ -0,0 +1,378 @@
|
||||||
|
# ADR 001: Coin Source Tracing
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
- 2020-07-09: Initial Draft
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The specification for IBC cross-chain fungible token transfers
|
||||||
|
([ICS20](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)), needs to
|
||||||
|
be aware of the origin of any token denomination in order to relay a `Packet` which contains the sender
|
||||||
|
and recipient addressed in the
|
||||||
|
[`FungibleTokenPacketData`](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures).
|
||||||
|
|
||||||
|
The Packet relay sending works based in 2 cases (per
|
||||||
|
[specification](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay) and [Colin Axnér](https://github.com/colin-axner)'s description):
|
||||||
|
|
||||||
|
1. Sender chain is acting as the source zone. The coins are transferred
|
||||||
|
to an escrow address (i.e locked) on the sender chain and then transferred
|
||||||
|
to the receiving chain through IBC TAO logic. It is expected that the
|
||||||
|
receiving chain will mint vouchers to the receiving address.
|
||||||
|
|
||||||
|
2. Sender chain is acting as the sink zone. The coins (vouchers) are burned
|
||||||
|
on the sender chain and then transferred to the receiving chain though IBC
|
||||||
|
TAO logic. It is expected that the receiving chain, which had previously
|
||||||
|
sent the original denomination, will unescrow the fungible token and send
|
||||||
|
it to the receiving address.
|
||||||
|
|
||||||
|
Another way of thinking of source and sink zones is through the token's
|
||||||
|
timeline. 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.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Assume the following channel connections exist and that all channels use the port ID `transfer`:
|
||||||
|
|
||||||
|
- chain `A` has channels with chain `B` and chain `C` with the IDs `channelToB` and `channelToC`, respectively
|
||||||
|
- chain `B` has channels with chain `A` and chain `C` with the IDs `channelToA` and `channelToC`, respectively
|
||||||
|
- chain `C` has channels with chain `A` and chain `B` with the IDs `channelToA` and `channelToB`, respectively
|
||||||
|
|
||||||
|
These steps of transfer between chains occur in the following order: `A -> B -> C -> A -> C`. In particular:
|
||||||
|
|
||||||
|
1. `A -> B`: sender chain is source zone. `A` sends packet with `denom` (escrowed on `A`), `B` receives `denom` and mints and sends voucher `transfer/channelToA/denom` to recipient.
|
||||||
|
2. `B -> C`: sender chain is source zone. `B` sends packet with `transfer/channelToA/denom` (escrowed on `B`), `C` receives `transfer/channelToA/denom` and mints and sends voucher `transfer/channelToB/transfer/channelToA/denom` to recipient.
|
||||||
|
3. `C -> A`: sender chain is source zone. `C` sends packet with `transfer/channelToB/transfer/channelToA/denom` (escrowed on `C`), `A` receives `transfer/channelToB/transfer/channelToA/denom` and mints and sends voucher `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom` to recipient.
|
||||||
|
4. `A -> C`: sender chain is sink zone. `A` sends packet with `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom` (burned on `A`), `C` receives `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom`, and unescrows and sends `transfer/channelToB/transfer/channelToA/denom` to recipient.
|
||||||
|
|
||||||
|
The token has a final denomination on chain `C` of `transfer/channelToB/transfer/channelToA/denom`, where `transfer/channelToB/transfer/channelToA` is the trace information.
|
||||||
|
|
||||||
|
In this context, upon a receive of a cross-chain fungible token transfer, if the sender chain is the source of the token, the protocol prefixes the denomination with the port and channel identifiers in the following format:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
prefix + denom = {destPortN}/{destChannelN}/.../{destPort0}/{destChannel0}/denom
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: transferring `100 uatom` from port `HubPort` and channel `HubChannel` on the Hub to
|
||||||
|
Ethermint's port `EthermintPort` and channel `EthermintChannel` results in `100
|
||||||
|
EthermintPort/EthermintChannel/uatom`, where `EthermintPort/EthermintChannel/uatom` is the new
|
||||||
|
denomination on the receiving chain.
|
||||||
|
|
||||||
|
In the case those tokens are transferred back to the Hub (i.e the **source** chain), the prefix is
|
||||||
|
trimmed and the token denomination updated to the original one.
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
|
||||||
|
The problem of adding additional information to the coin denomination is twofold:
|
||||||
|
|
||||||
|
1. The ever increasing length if tokens are transferred to zones other than the source:
|
||||||
|
|
||||||
|
If a token is transferred `n` times via IBC to a sink chain, the token denom will contain `n` pairs
|
||||||
|
of prefixes, as shown on the format example above. This poses a problem because, while port and
|
||||||
|
channel identifiers have a maximum length of 64 each, the SDK `Coin` type only accepts denoms up to
|
||||||
|
64 characters. Thus, a single cross-chain token, which again, is composed by the port and channels
|
||||||
|
identifiers plus the base denomination, can exceed the length validation for the SDK `Coins`.
|
||||||
|
|
||||||
|
This can result in undesired behaviours such as tokens not being able to be transferred to multiple
|
||||||
|
sink chains if the denomination exceeds the length or unexpected `panics` due to denomination
|
||||||
|
validation failing on the receiving chain.
|
||||||
|
|
||||||
|
2. The existence of special characters and uppercase letters on the denomination:
|
||||||
|
|
||||||
|
In the SDK every time a `Coin` is initialized through the constructor function `NewCoin`, a validation
|
||||||
|
of a coin's denom is performed according to a
|
||||||
|
[Regex](https://github.com/cosmos/cosmos-sdk/blob/a940214a4923a3bf9a9161cd14bd3072299cd0c9/types/coin.go#L583),
|
||||||
|
where only lowercase alphanumeric characters are accepted. While this is desirable for native denominations
|
||||||
|
to keep a clean UX, it presents a challenge for IBC as ports and channels might be randomly
|
||||||
|
generated with special and uppercase characters as per the [ICS 024 - Host
|
||||||
|
Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements#paths-identifiers-separators)
|
||||||
|
specification.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
The issues outlined above, are applicable only to SDK-based chains, and thus the proposed solution
|
||||||
|
are do not require specification changes that would result in modification to other implementations
|
||||||
|
of the ICS20 spec.
|
||||||
|
|
||||||
|
Instead of adding the identifiers on the coin denomination directly, the proposed solution hashes
|
||||||
|
the denomination prefix in order to get a consistent length for all the cross-chain fungible tokens.
|
||||||
|
|
||||||
|
This will be used for internal storage only, and when transferred via IBC to a different chain, the
|
||||||
|
denomination specified on the packed data will be the full prefix path of the identifiers needed to
|
||||||
|
trace the token back to the originating chain, as specified on ICS20.
|
||||||
|
|
||||||
|
The new proposed format will be the following:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
ibcDenom = "ibc/" + hash(trace + "/" + base denom)
|
||||||
|
```
|
||||||
|
|
||||||
|
The hash function will be a SHA256 hash of the fields of the `DenomTrace`:
|
||||||
|
|
||||||
|
```protobuf
|
||||||
|
// DenomTrace contains the base denomination for ICS20 fungible tokens and the source tracing
|
||||||
|
// information
|
||||||
|
message DenomTrace {
|
||||||
|
// chain of port/channel identifiers used for tracing the source of the fungible token
|
||||||
|
string trace = 1;
|
||||||
|
// base denomination of the relayed fungible token
|
||||||
|
string base_denom = 2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `IBCDenom` function constructs the `Coin` denomination used when creating the ICS20 fungible token packet data:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// Hash returns the hex bytes of the SHA256 hash of the DenomTrace fields.
|
||||||
|
func (dt DenomTrace) Hash() tmbytes.HexBytes {
|
||||||
|
return tmhash.Sum(dt.Trace + "/" + dt.BaseDenom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IBCDenom a coin denomination for an ICS20 fungible token in the format 'ibc/{hash(trace + baseDenom)}'. If the trace is empty, it will return the base denomination.
|
||||||
|
func (dt DenomTrace) IBCDenom() string {
|
||||||
|
if dt.Trace != "" {
|
||||||
|
return fmt.Sprintf("ibc/%s", dt.Hash())
|
||||||
|
}
|
||||||
|
return dt.BaseDenom
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to trim the denomination trace prefix when sending/receiving fungible tokens, the `RemovePrefix` function is provided.
|
||||||
|
|
||||||
|
> NOTE: the prefix addition must be done on the client side.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// RemovePrefix trims the first portID/channelID pair from the trace info. If the trace is already empty it will perform a no-op. If the trace is incorrectly constructed or doesn't have separators it will return an error.
|
||||||
|
func (dt *DenomTrace) RemovePrefix() error {
|
||||||
|
if dt.Trace == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
traceSplit := strings.SplitN(dt.Trace, "/", 3)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
// NOTE: other cases are checked during msg validation
|
||||||
|
case len(traceSplit) == 2:
|
||||||
|
dt.Trace = ""
|
||||||
|
case len(traceSplit) == 3:
|
||||||
|
dt.Trace = traceSplit[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `x/ibc-transfer` Changes
|
||||||
|
|
||||||
|
In order to retrieve the trace information from an IBC denomination, a lookup table needs to be
|
||||||
|
added to the `ibc-transfer` module. These values need to also be persisted between upgrades, meaning
|
||||||
|
that a new `[]DenomTrace` `GenesisState` field state needs to be added to the module:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// GetDenomTrace retrieves the full identifiers trace and base denomination from the store.
|
||||||
|
func (k Keeper) GetDenomTrace(ctx Context, denomTraceHash []byte) (DenomTrace, bool) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
bz := store.Get(types.KeyDenomTrace(traceHash))
|
||||||
|
if bz == nil {
|
||||||
|
return &DenomTrace, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var denomTrace DenomTrace
|
||||||
|
k.cdc.MustUnmarshalBinaryBare(bz, &denomTrace)
|
||||||
|
return denomTrace, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasDenomTrace checks if a the key with the given trace hash exists on the store.
|
||||||
|
func (k Keeper) HasDenomTrace(ctx Context, denomTraceHash []byte) bool {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
return store.Has(types.KeyTrace(denomTraceHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDenomTrace sets a new {trace hash -> trace} pair to the store.
|
||||||
|
func (k Keeper) SetDenomTrace(ctx Context, denomTrace DenomTrace) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
bz := k.cdc.MustMarshalBinaryBare(&denomTrace)
|
||||||
|
store.Set(types.KeyTrace(denomTrace.Hash()), bz)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `MsgTransfer` will validate that the `Coin` denomination from the `Token` field contains a valid
|
||||||
|
hash, if the trace info is provided, or that the base denominations matches:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
func (msg MsgTransfer) ValidateBasic() error {
|
||||||
|
// ...
|
||||||
|
if err := msg.Trace.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ValidateIBCDenom(msg.Token.Denom, msg.Trace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// ValidateIBCDenom checks that the denomination for an IBC fungible token is valid. It returns error if the trace `hash` is invalid
|
||||||
|
func ValidateIBCDenom(denom string, trace DenomTrace) error {
|
||||||
|
// Validate that base denominations are equal if the trace info is not provided
|
||||||
|
if trace.Trace == "" {
|
||||||
|
if trace.BaseDenom != denom {
|
||||||
|
return Wrapf(
|
||||||
|
ErrInvalidDenomForTransfer,
|
||||||
|
"token denom must match the trace base denom (%s ≠ %s)",
|
||||||
|
denom, trace.BaseDenom,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
denomSplit := strings.SplitN(denom, "/", 2)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case denomSplit[0] != "ibc":
|
||||||
|
return Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom)
|
||||||
|
case len(denomSplit) == 2:
|
||||||
|
return tmtypes.ValidateHash([]byte(denomSplit[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
denomTraceHash := denomSplit[1]
|
||||||
|
traceHash := trace.Hash()
|
||||||
|
if !bytes.Equal(traceHash.Bytes(), denomTraceHash.Bytes()) {
|
||||||
|
return Errorf("token denomination trace hash mismatch, expected %s got %s", traceHash, denomTraceHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The denomination trace info only needs to be updated when token is received:
|
||||||
|
|
||||||
|
- Receiver is **source** chain: The receiver created the token and must have the trace lookup already stored (if necessary _ie_ native token case wouldn't need a lookup).
|
||||||
|
- Receiver is **not source** chain: Store the received info. For example, during step 1, when chain `B` receives `transfer/channelToA/denom`.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// OnRecvPacket
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// This is the prefix that would have been prefixed to the denomination
|
||||||
|
// on sender chain IF and only if the token originally came from the
|
||||||
|
// receiving chain.
|
||||||
|
//
|
||||||
|
// NOTE: We use SourcePort and SourceChannel here, because the counterparty
|
||||||
|
// chain would have prefixed with DestPort and DestChannel when originally
|
||||||
|
// receiving this coin as seen in the "sender chain is the source" condition.
|
||||||
|
voucherPrefix := GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel())
|
||||||
|
|
||||||
|
if ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) {
|
||||||
|
// sender chain is not the source, unescrow tokens
|
||||||
|
|
||||||
|
// remove prefix added by sender chain
|
||||||
|
if err := denomTrace.RemovePrefix(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: since the sender is a sink chain, we already know the unprefixed denomination trace info
|
||||||
|
|
||||||
|
token := sdk.NewCoin(denomTrace.IBCDenom(), sdk.NewIntFromUint64(data.Amount))
|
||||||
|
|
||||||
|
// unescrow tokens
|
||||||
|
escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel())
|
||||||
|
return k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sender chain is the source, mint vouchers
|
||||||
|
|
||||||
|
// construct the denomination trace from the full raw denomination
|
||||||
|
denomTrace := NewDenomTraceFromRawDenom(data.Denom)
|
||||||
|
|
||||||
|
// since SendPacket did not prefix the denomination with the voucherPrefix, we must add it here
|
||||||
|
denomTrace.AddPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
||||||
|
|
||||||
|
// set the value to the lookup table if not stored already
|
||||||
|
traceHash := denomTrace.Hash()
|
||||||
|
if !k.HasDenomTrace(ctx, traceHash) {
|
||||||
|
k.SetDenomTrace(ctx, traceHash, denomTrace)
|
||||||
|
}
|
||||||
|
|
||||||
|
voucher := sdk.NewCoin(denomTrace.IBCDenom(), sdk.NewIntFromUint64(data.Amount))
|
||||||
|
|
||||||
|
// mint new tokens if the source of the transfer is the same chain
|
||||||
|
if err := k.bankKeeper.MintCoins(
|
||||||
|
ctx, types.ModuleName, sdk.NewCoins(voucher),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// send to receiver
|
||||||
|
return k.bankKeeper.SendCoinsFromModuleToAccount(
|
||||||
|
ctx, types.ModuleName, receiver, sdk.NewCoins(voucher),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
func NewDenomTraceFromRawDenom(denom string) DenomTrace{
|
||||||
|
denomSplit := strings.Split(denom, "/")
|
||||||
|
trace := ""
|
||||||
|
if len(denomSplit) > 1 {
|
||||||
|
trace = strings.Join(denomSplit[:len(denomSplit)-1], "/")
|
||||||
|
}
|
||||||
|
return DenomTrace{
|
||||||
|
BaseDenom: denomSplit[len(denomSplit)-1],
|
||||||
|
Trace: trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One final remark is that the `FungibleTokenPacketData` will remain the same, i.e with the prefixed full denomination, since the receiving chain may not be an SDK-based chain.
|
||||||
|
|
||||||
|
### Coin Changes
|
||||||
|
|
||||||
|
The coin denomination validation will need to be updated to reflect these changes. In particular, the denomination validation
|
||||||
|
function will now:
|
||||||
|
|
||||||
|
- Accept slash separators (`"/"`) and uppercase characters (due to the `HexBytes` format)
|
||||||
|
- Bump the maximum character length to 64
|
||||||
|
|
||||||
|
Additional validation logic, such as verifying the length of the hash, the may be added to the bank module in the future if the [custom base denomination validation](https://github.com/cosmos/cosmos-sdk/pull/6755) is integrated into the SDK.
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- Clearer separation of the source tracing behaviour of the token (transfer prefix) from the original
|
||||||
|
`Coin` denomination
|
||||||
|
- Consistent validation of `Coin` fields (i.e no special characters, fixed max length)
|
||||||
|
- Cleaner `Coin` and standard denominations for IBC
|
||||||
|
- No additional fields to SDK `Coin`
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- Store each set of tracing denomination identifiers on the `ibc-transfer` module store
|
||||||
|
- Clients will have to fetch the base denomination every time they receive a new relayed fungible token over IBC. This can be mitigated using a map/cache for already seen hashes on the client side. Other forms of mitigation, would be opening a websocket connection subscribe to incoming events.
|
||||||
|
|
||||||
|
### Neutral
|
||||||
|
|
||||||
|
- Slight difference with the ICS20 spec
|
||||||
|
- Additional validation logic for IBC coins on the `ibc-transfer` module
|
||||||
|
- Additional genesis fields
|
||||||
|
- Slightly increases the gas usage on cross-chain transfers due to access to the store. This should
|
||||||
|
be inter-block cached if transfers are frequent.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ICS 20 - Fungible token transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)
|
||||||
|
- [Custom Coin Denomination validation](https://github.com/cosmos/cosmos-sdk/pull/6755)
|
Loading…
Reference in New Issue