fix ibc-transfer source bug (#6865)
* update proto and refactor types test * cleanup SendTransfer * fix build and prep to update handler tests * fix handler tests * fix tests * update spec * fix lint * Apply suggestions from code review Co-authored-by: Aditya <adityasripal@gmail.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * rename amount to token * merge send transfer and createOutgoingPacket * apply rest of @fedekunze review suggestions * apply review suggestions from @AdityaSripal * update spec to token renaming * split token in denom and amount when creating fungible token packet * remove source flag, add prefix on recv * fix lint * fix proto numbers * Update x/ibc-transfer/keeper/relay.go Co-authored-by: Christopher Goes <cwgoes@pluranimity.org> * apply @cwgoes review suggestions * Apply suggestions from code review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * apply @fedekunze suggestions * Apply suggestions from code review * fix lint * apply @AdityaSripal review requests * Update x/ibc-transfer/types/coin.go * apply @AdityaSpripal last suggestions Co-authored-by: Aditya <adityasripal@gmail.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Christopher Goes <cwgoes@pluranimity.org>
This commit is contained in:
parent
8e67a5d23b
commit
b3bbca343f
@ -6,26 +6,19 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/ibc-transfer/types";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/cosmos.proto";
|
||||
|
||||
// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between ICS20 enabled chains.
|
||||
// See ICS Spec here: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between
|
||||
// ICS20 enabled chains. See ICS Spec here:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
message MsgTransfer {
|
||||
// the port on which the packet will be sent
|
||||
string source_port = 1 [
|
||||
(gogoproto.moretags) = "yaml:\"source_port\""
|
||||
];
|
||||
string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""];
|
||||
// the channel by which the packet will be sent
|
||||
string source_channel = 2 [
|
||||
(gogoproto.moretags) = "yaml:\"source_channel\""
|
||||
];
|
||||
string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""];
|
||||
// the tokens to be transferred
|
||||
repeated cosmos.Coin amount = 3 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
|
||||
];
|
||||
cosmos.Coin token = 3 [(gogoproto.nullable) = false];
|
||||
// the sender address
|
||||
bytes sender = 4 [
|
||||
(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"
|
||||
];
|
||||
bytes sender = 4
|
||||
[(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
|
||||
// the recipient address on the destination chain
|
||||
string receiver = 5;
|
||||
// Timeout height relative to the current block height.
|
||||
@ -33,27 +26,29 @@ message MsgTransfer {
|
||||
uint64 timeout_height = 6 [(gogoproto.moretags) = "yaml:\"timeout_height\""];
|
||||
// Timeout timestamp (in nanoseconds) relative to the current block timestamp.
|
||||
// The timeout is disabled when set to 0.
|
||||
uint64 timeout_timestamp = 7 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""];
|
||||
uint64 timeout_timestamp = 7
|
||||
[(gogoproto.moretags) = "yaml:\"timeout_timestamp\""];
|
||||
}
|
||||
|
||||
// FungibleTokenPacketData defines a struct for the packet payload
|
||||
// See FungibleTokenPacketData spec: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
// See FungibleTokenPacketData spec:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
message FungibleTokenPacketData {
|
||||
// the tokens to be transferred
|
||||
repeated cosmos.Coin amount = 1 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
|
||||
];
|
||||
// the token denomination to be transferred
|
||||
string denom = 1;
|
||||
// the token amount to be transferred
|
||||
uint64 amount = 2;
|
||||
// the sender address
|
||||
string sender = 2;
|
||||
string sender = 3;
|
||||
// the recipient address on the destination chain
|
||||
string receiver = 3;
|
||||
string receiver = 4;
|
||||
}
|
||||
|
||||
// FungibleTokenPacketAcknowledgement contains a boolean success flag and an optional error msg
|
||||
// error msg is empty string on success
|
||||
// See spec for onAcknowledgePacket: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
// FungibleTokenPacketAcknowledgement contains a boolean success flag and an
|
||||
// optional error msg error msg is empty string on success See spec for
|
||||
// onAcknowledgePacket:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
message FungibleTokenPacketAcknowledgement {
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ to the counterparty channel. Any timeout set to 0 is disabled.`),
|
||||
srcChannel := args[1]
|
||||
receiver := args[2]
|
||||
|
||||
coins, err := sdk.ParseCoins(args[3])
|
||||
coin, err := sdk.ParseCoin(args[3])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -82,7 +82,7 @@ to the counterparty channel. Any timeout set to 0 is disabled.`),
|
||||
}
|
||||
|
||||
msg := types.NewMsgTransfer(
|
||||
srcPort, srcChannel, coins, sender, receiver, timeoutHeight, timeoutTimestamp,
|
||||
srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp,
|
||||
)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return err
|
||||
|
||||
@ -21,7 +21,7 @@ func RegisterRoutes(clientCtx client.Context, r *mux.Router) {
|
||||
// TransferTxReq defines the properties of a transfer tx request's body.
|
||||
type TransferTxReq struct {
|
||||
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
|
||||
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||
Amount sdk.Coin `json:"amount" yaml:"amount"`
|
||||
Receiver string `json:"receiver" yaml:"receiver"`
|
||||
TimeoutHeight uint64 `json:"timeout_height" yaml:"timeout_height"`
|
||||
TimeoutTimestamp uint64 `json:"timeout_timestamp" yaml:"timeout_timestamp"`
|
||||
|
||||
@ -24,12 +24,12 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
// See createOutgoingPacket in spec:https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
func handleMsgTransfer(ctx sdk.Context, k keeper.Keeper, msg *types.MsgTransfer) (*sdk.Result, error) {
|
||||
if err := k.SendTransfer(
|
||||
ctx, msg.SourcePort, msg.SourceChannel, msg.Amount, msg.Sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp,
|
||||
ctx, msg.SourcePort, msg.SourceChannel, msg.Token, msg.Sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.Logger(ctx).Info("IBC transfer: %s from %s to %s", msg.Amount, msg.Sender, msg.Receiver)
|
||||
k.Logger(ctx).Info("IBC transfer: %s from %s to %s", msg.Token, msg.Sender, msg.Receiver)
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
package transfer_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ibctransfer "github.com/cosmos/cosmos-sdk/x/ibc-transfer"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
|
||||
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
@ -29,41 +28,54 @@ func (suite *HandlerTestSuite) SetupTest() {
|
||||
}
|
||||
|
||||
// constructs a send from chainA to chainB on the established channel/connection
|
||||
// and sends the coins back from chainB to chainA.
|
||||
// FIX: this test currently passes because source is incorrectly determined
|
||||
// by the ibc-transfer module, so what actually occurs is chainA and chainB
|
||||
// send coins to each other, but no coins are ever sent back. This can be
|
||||
// fixed by receving and acknowledeging the send on the counterparty chain.
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/6827
|
||||
// and sends the same coin back from chainB to chainA.
|
||||
func (suite *HandlerTestSuite) TestHandleMsgTransfer() {
|
||||
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
handlerA := ibctransfer.NewHandler(suite.chainA.App.TransferKeeper)
|
||||
originalBalance := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom)
|
||||
|
||||
coinToSendToB := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%s/%s/%s", channelB.PortID, channelB.ID, sdk.DefaultBondDenom), sdk.NewInt(100)))
|
||||
coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
|
||||
|
||||
// send from chainA to chainB
|
||||
msg := types.NewMsgTransfer(channelA.PortID, channelA.ID, coinToSendToB, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), 110, 0)
|
||||
|
||||
res, err := handlerA(suite.chainA.GetContext(), msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res, "%+v", res) // successfully executed
|
||||
|
||||
err = suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
handlerB := ibctransfer.NewHandler(suite.chainB.App.TransferKeeper)
|
||||
// relay send
|
||||
fungibleTokenPacket := types.NewFungibleTokenPacketData(coinToSendToB.Denom, coinToSendToB.Amount.Uint64(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String())
|
||||
packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 110, 0)
|
||||
ack := types.FungibleTokenPacketAcknowledgement{Success: true}
|
||||
err = suite.coordinator.RelayPacket(suite.chainA, suite.chainB, clientA, clientB, packet, ack.GetBytes())
|
||||
suite.Require().NoError(err) // relay committed
|
||||
|
||||
coinToSendBackToA := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%s/%s/%s", channelA.PortID, channelA.ID, sdk.DefaultBondDenom), sdk.NewInt(100)))
|
||||
// check that voucher exists on chain
|
||||
voucherDenom := types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), sdk.DefaultBondDenom)
|
||||
balance := suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), voucherDenom)
|
||||
|
||||
coinToSendBackToA := types.GetTransferCoin(channelB.PortID, channelB.ID, sdk.DefaultBondDenom, 100)
|
||||
suite.Require().Equal(coinToSendBackToA, balance)
|
||||
|
||||
// send from chainB back to chainA
|
||||
msg = types.NewMsgTransfer(channelA.PortID, channelA.ID, coinToSendBackToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), 110, 0)
|
||||
|
||||
res, err = handlerB(suite.chainB.GetContext(), msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res, "%+v", res) // successfully executed
|
||||
msg = types.NewMsgTransfer(channelB.PortID, channelB.ID, coinToSendBackToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), 110, 0)
|
||||
|
||||
err = suite.coordinator.SendMsgs(suite.chainB, suite.chainA, clientA, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
// relay send
|
||||
fungibleTokenPacket = types.NewFungibleTokenPacketData(coinToSendBackToA.Denom, coinToSendBackToA.Amount.Uint64(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String())
|
||||
packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, 110, 0)
|
||||
err = suite.coordinator.RelayPacket(suite.chainB, suite.chainA, clientB, clientA, packet, ack.GetBytes())
|
||||
suite.Require().NoError(err) // relay committed
|
||||
|
||||
balance = suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom)
|
||||
|
||||
// check that the balance on chainA returned back to the original state
|
||||
suite.Require().Equal(originalBalance, balance)
|
||||
|
||||
// check that module account escrow address is empty
|
||||
escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel())
|
||||
balance = suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom)
|
||||
suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), balance)
|
||||
}
|
||||
|
||||
func TestHandlerTestSuite(t *testing.T) {
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
|
||||
@ -12,19 +10,42 @@ import (
|
||||
|
||||
// SendTransfer handles transfer sending logic. There are 2 possible cases:
|
||||
//
|
||||
// 1. Sender chain is the source chain of the coins (i.e where they were minted): the coins
|
||||
// are transferred to an escrow address (i.e locked) on the sender chain and then
|
||||
// transferred to the destination chain (i.e not the source chain) via a packet
|
||||
// with the corresponding fungible token data.
|
||||
// 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. Coins are not native from the sender chain (i.e tokens sent where transferred over
|
||||
// through IBC already): the coins are burned and then a packet is sent to the
|
||||
// source chain of the tokens.
|
||||
// 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:
|
||||
// These steps of transfer occur: A -> B -> C -> A -> C -> B -> A
|
||||
//
|
||||
// 1. A -> B : sender chain is source zone. Denom upon receiving: 'B/denom'
|
||||
// 2. B -> C : sender chain is source zone. Denom upon receiving: 'C/B/denom'
|
||||
// 3. C -> A : sender chain is source zone. Denom upon receiving: 'A/C/B/denom'
|
||||
// 4. A -> C : sender chain is sink zone. Denom upon receiving: 'C/B/denom'
|
||||
// 5. C -> B : sender chain is sink zone. Denom upon receiving: 'B/denom'
|
||||
// 6. B -> A : sender chain is sink zone. Denom upon receiving: 'denom'
|
||||
|
||||
func (k Keeper) SendTransfer(
|
||||
ctx sdk.Context,
|
||||
sourcePort,
|
||||
sourceChannel string,
|
||||
amount sdk.Coins,
|
||||
token sdk.Coin,
|
||||
sender sdk.AccAddress,
|
||||
receiver string,
|
||||
timeoutHeight,
|
||||
@ -47,81 +68,39 @@ func (k Keeper) SendTransfer(
|
||||
)
|
||||
}
|
||||
|
||||
return k.createOutgoingPacket(
|
||||
ctx, sequence, sourcePort, sourceChannel, destinationPort, destinationChannel,
|
||||
amount, sender, receiver, timeoutHeight, timeoutTimestamp,
|
||||
)
|
||||
}
|
||||
// begin createOutgoingPacket logic
|
||||
// See spec for this logic: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
|
||||
// See spec for this function: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
func (k Keeper) createOutgoingPacket(
|
||||
ctx sdk.Context,
|
||||
seq uint64,
|
||||
sourcePort, sourceChannel,
|
||||
destinationPort, destinationChannel string,
|
||||
amount sdk.Coins,
|
||||
sender sdk.AccAddress,
|
||||
receiver string,
|
||||
timeoutHeight, timeoutTimestamp uint64,
|
||||
) error {
|
||||
channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel))
|
||||
if !ok {
|
||||
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
|
||||
}
|
||||
// NOTE:
|
||||
// - Coins transferred from the destination chain should have their denomination
|
||||
// prefixed with source port and channel IDs.
|
||||
// - Coins transferred from the source chain can have their denomination
|
||||
// clear from prefixes when transferred to the escrow account (i.e when they are
|
||||
// locked) BUT MUST have the destination port and channel ID when constructing
|
||||
// the packet data.
|
||||
if len(amount) != 1 {
|
||||
return sdkerrors.Wrapf(types.ErrOnlyOneDenomAllowed, "%d denoms included", len(amount))
|
||||
}
|
||||
|
||||
prefix := types.GetDenomPrefix(destinationPort, destinationChannel)
|
||||
source := strings.HasPrefix(amount[0].Denom, prefix)
|
||||
// NOTE: SendTransfer simply sends the denomination as it exists on its own
|
||||
// chain inside the packet data. The receiving chain will perform denom
|
||||
// prefixing as necessary.
|
||||
|
||||
if source {
|
||||
// clear the denomination from the prefix to send the coins to the escrow account
|
||||
coins := make(sdk.Coins, len(amount))
|
||||
for i, coin := range amount {
|
||||
if strings.HasPrefix(coin.Denom, prefix) {
|
||||
coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount)
|
||||
} else {
|
||||
coins[i] = coin
|
||||
}
|
||||
}
|
||||
|
||||
// escrow tokens if the destination chain is the same as the sender's
|
||||
if types.SenderChainIsSource(sourcePort, sourceChannel, token.Denom) {
|
||||
// create the escrow address for the tokens
|
||||
escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel)
|
||||
|
||||
// escrow source tokens. It fails if balance insufficient.
|
||||
if err := k.bankKeeper.SendCoins(
|
||||
ctx, sender, escrowAddress, coins,
|
||||
ctx, sender, escrowAddress, sdk.NewCoins(token),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// build the receiving denomination prefix if it's not present
|
||||
prefix = types.GetDenomPrefix(sourcePort, sourceChannel)
|
||||
for _, coin := range amount {
|
||||
if !strings.HasPrefix(coin.Denom, prefix) {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidDenomForTransfer, "denom was: %s", coin.Denom)
|
||||
}
|
||||
}
|
||||
|
||||
// transfer the coins to the module account and burn them
|
||||
if err := k.bankKeeper.SendCoinsFromAccountToModule(
|
||||
ctx, sender, types.ModuleName, amount,
|
||||
ctx, sender, types.ModuleName, sdk.NewCoins(token),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// burn vouchers from the sender's balance if the source is from another chain
|
||||
if err := k.bankKeeper.BurnCoins(
|
||||
ctx, types.ModuleName, amount,
|
||||
ctx, types.ModuleName, sdk.NewCoins(token),
|
||||
); err != nil {
|
||||
// NOTE: should not happen as the module account was
|
||||
// retrieved on the step above and it has enough balace
|
||||
@ -131,12 +110,12 @@ func (k Keeper) createOutgoingPacket(
|
||||
}
|
||||
|
||||
packetData := types.NewFungibleTokenPacketData(
|
||||
amount, sender.String(), receiver,
|
||||
token.Denom, token.Amount.Uint64(), sender.String(), receiver,
|
||||
)
|
||||
|
||||
packet := channeltypes.NewPacket(
|
||||
packetData.GetBytes(),
|
||||
seq,
|
||||
sequence,
|
||||
sourcePort,
|
||||
sourceChannel,
|
||||
destinationPort,
|
||||
@ -148,76 +127,87 @@ func (k Keeper) createOutgoingPacket(
|
||||
return k.channelKeeper.SendPacket(ctx, channelCap, packet)
|
||||
}
|
||||
|
||||
// OnRecvPacket processes a cross chain fungible token transfer. If the
|
||||
// sender chain is the source of minted tokens then vouchers will be minted
|
||||
// and sent to the receiving address. Otherwise if the sender chain is sending
|
||||
// back tokens this chain originally transferred to it, the tokens are
|
||||
// unescrowed and sent to the receiving address.
|
||||
func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error {
|
||||
// NOTE: packet data type already checked in handler.go
|
||||
|
||||
if len(data.Amount) != 1 {
|
||||
return sdkerrors.Wrapf(types.ErrOnlyOneDenomAllowed, "%d denoms included", len(data.Amount))
|
||||
}
|
||||
|
||||
prefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
||||
source := strings.HasPrefix(data.Amount[0].Denom, prefix)
|
||||
|
||||
// decode the receiver address
|
||||
receiver, err := sdk.AccAddressFromBech32(data.Receiver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if source {
|
||||
// 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.
|
||||
|
||||
// mint new tokens if the source of the transfer is the same chain
|
||||
if err := k.bankKeeper.MintCoins(
|
||||
ctx, types.ModuleName, data.Amount,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) {
|
||||
// sender chain is not the source, unescrow tokens
|
||||
|
||||
// send to receiver
|
||||
return k.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
ctx, types.ModuleName, receiver, data.Amount,
|
||||
)
|
||||
// remove prefix added by sender chain
|
||||
voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel())
|
||||
unprefixedDenom := data.Denom[len(voucherPrefix):]
|
||||
token := sdk.NewCoin(unprefixedDenom, sdk.NewIntFromUint64(data.Amount))
|
||||
|
||||
// unescrow tokens
|
||||
escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel())
|
||||
return k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token))
|
||||
}
|
||||
|
||||
// check the denom prefix
|
||||
prefix = types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel())
|
||||
coins := make(sdk.Coins, len(data.Amount))
|
||||
for i, coin := range data.Amount {
|
||||
if !strings.HasPrefix(coin.Denom, prefix) {
|
||||
return sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInvalidCoins,
|
||||
"%s doesn't contain the prefix '%s'", coin.Denom, prefix,
|
||||
)
|
||||
}
|
||||
coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount)
|
||||
// sender chain is the source, mint vouchers
|
||||
|
||||
// since SendPacket did not prefix the denomination, we must prefix denomination here
|
||||
sourcePrefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
||||
// NOTE: sourcePrefix contains the trailing "/"
|
||||
prefixedDenom := sourcePrefix + data.Denom
|
||||
voucher := sdk.NewCoin(prefixedDenom, 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
|
||||
}
|
||||
|
||||
// unescrow tokens
|
||||
escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel())
|
||||
return k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, coins)
|
||||
// send to receiver
|
||||
return k.bankKeeper.SendCoinsFromModuleToAccount(
|
||||
ctx, types.ModuleName, receiver, sdk.NewCoins(voucher),
|
||||
)
|
||||
}
|
||||
|
||||
// OnAcknowledgementPacket responds to the the success or failure of a packet
|
||||
// acknowledgement written on the receiving chain. If the acknowledgement
|
||||
// was a success then nothing occurs. If the acknowledgement failed, then
|
||||
// the sender is refunded their tokens using the refundPacketToken function.
|
||||
func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData, ack types.FungibleTokenPacketAcknowledgement) error {
|
||||
if !ack.Success {
|
||||
return k.refundPacketAmount(ctx, packet, data)
|
||||
return k.refundPacketToken(ctx, packet, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnTimeoutPacket refunds the sender since the original packet sent was
|
||||
// never received and has been timed out.
|
||||
func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error {
|
||||
return k.refundPacketAmount(ctx, packet, data)
|
||||
return k.refundPacketToken(ctx, packet, data)
|
||||
}
|
||||
|
||||
func (k Keeper) refundPacketAmount(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error {
|
||||
// refundPacketToken will unescrow and send back the tokens back to sender
|
||||
// if the sending chain was the source chain. Otherwise, the sent tokens
|
||||
// were burnt in the original send so new tokens are minted and sent to
|
||||
// the sending address.
|
||||
func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error {
|
||||
// NOTE: packet data type already checked in handler.go
|
||||
|
||||
if len(data.Amount) != 1 {
|
||||
return sdkerrors.Wrapf(types.ErrOnlyOneDenomAllowed, "%d denoms included", len(data.Amount))
|
||||
}
|
||||
|
||||
// check the denom prefix
|
||||
prefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
||||
source := strings.HasPrefix(data.Amount[0].Denom, prefix)
|
||||
token := sdk.NewCoin(data.Denom, sdk.NewIntFromUint64(data.Amount))
|
||||
|
||||
// decode the sender address
|
||||
sender, err := sdk.AccAddressFromBech32(data.Sender)
|
||||
@ -225,27 +215,18 @@ func (k Keeper) refundPacketAmount(ctx sdk.Context, packet channeltypes.Packet,
|
||||
return err
|
||||
}
|
||||
|
||||
if source {
|
||||
coins := make(sdk.Coins, len(data.Amount))
|
||||
for i, coin := range data.Amount {
|
||||
coin := coin
|
||||
if !strings.HasPrefix(coin.Denom, prefix) {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "%s doesn't contain the prefix '%s'", coin.Denom, prefix)
|
||||
}
|
||||
coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount)
|
||||
}
|
||||
|
||||
if types.SenderChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), token.Denom) {
|
||||
// unescrow tokens back to sender
|
||||
escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel())
|
||||
return k.bankKeeper.SendCoins(ctx, escrowAddress, sender, coins)
|
||||
return k.bankKeeper.SendCoins(ctx, escrowAddress, sender, sdk.NewCoins(token))
|
||||
}
|
||||
|
||||
// mint vouchers back to sender
|
||||
if err := k.bankKeeper.MintCoins(
|
||||
ctx, types.ModuleName, data.Amount,
|
||||
ctx, types.ModuleName, sdk.NewCoins(token),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, data.Amount)
|
||||
return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, sdk.NewCoins(token))
|
||||
}
|
||||
|
||||
@ -35,20 +35,20 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
|
||||
{"successful transfer from source chain",
|
||||
func() {
|
||||
_, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
amount = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
amount = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, true},
|
||||
{"successful transfer with coins from counterparty chain",
|
||||
func() {
|
||||
// send coins from chainA back to chainB
|
||||
_, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
amount = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
amount = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
}, false, true},
|
||||
{"source channel not found",
|
||||
func() {
|
||||
// channel references wrong ID
|
||||
_, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
channelA.ID = ibctesting.InvalidID
|
||||
amount = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
amount = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, false},
|
||||
{"next seq send not found",
|
||||
func() {
|
||||
@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
|
||||
channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, channeltypes.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.ChannelVersion),
|
||||
)
|
||||
suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID)
|
||||
amount = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
amount = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, false},
|
||||
|
||||
// createOutgoingPacket tests
|
||||
@ -70,13 +70,13 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
|
||||
{"send coins failed",
|
||||
func() {
|
||||
_, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
amount = ibctesting.NewTransferCoins(channelB, "randomdenom", 100)
|
||||
amount = types.GetTransferCoin(channelB, "randomdenom", 100)
|
||||
}, true, false},
|
||||
// - receiving chain
|
||||
{"send from module account failed",
|
||||
func() {
|
||||
_, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
amount = ibctesting.NewTransferCoins(channelA, "randomdenom", 100)
|
||||
amount = types.GetTransferCoin(channelA, "randomdenom", 100)
|
||||
}, false, false},
|
||||
{"channel capability not found",
|
||||
func() {
|
||||
@ -98,7 +98,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
|
||||
|
||||
if !tc.source {
|
||||
// send coins from chainB to chainA
|
||||
coinFromBToA := ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
coinFromBToA := types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
transferMsg := types.NewMsgTransfer(channelB.PortID, channelB.ID, coinFromBToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), 110, 0)
|
||||
err = suite.coordinator.SendMsgs(suite.chainB, suite.chainA, channelA.ClientID, transferMsg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
@ -163,12 +163,12 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
|
||||
// onRecvPacket
|
||||
// - coins from source chain (chainA)
|
||||
{"failure: mint zero coins", func() {
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 0)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 0)
|
||||
}, true, false},
|
||||
|
||||
// - coins being sent back to original chain (chainB)
|
||||
{"tries to unescrow more tokens than allowed", func() {
|
||||
coins = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 1000000)
|
||||
coins = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 1000000)
|
||||
}, false, false},
|
||||
}
|
||||
|
||||
@ -184,38 +184,23 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
|
||||
|
||||
if !tc.source {
|
||||
// send coins from chainB to chainA, receive them, acknowledge them, and send back to chainB
|
||||
coinFromBToA := ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
coinFromBToA := types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
transferMsg := types.NewMsgTransfer(channelB.PortID, channelB.ID, coinFromBToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), 110, 0)
|
||||
err := suite.coordinator.SendMsgs(suite.chainB, suite.chainA, channelA.ClientID, transferMsg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
// receive coins on chainA from chainB
|
||||
// relay send packet
|
||||
fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String())
|
||||
packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, 110, 0)
|
||||
|
||||
// get proof of packet commitment from chainB
|
||||
packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
|
||||
proof, proofHeight := suite.chainB.QueryProof(packetKey)
|
||||
|
||||
recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainA.SenderAccount.GetAddress())
|
||||
err = suite.coordinator.SendMsgs(suite.chainA, suite.chainB, channelB.ClientID, recvMsg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
// get proof of acknowledgement on chainA
|
||||
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
|
||||
proof, proofHeight = suite.chainA.QueryProof(packetKey)
|
||||
|
||||
// acknowledge on chainB the receive that happened on chainA
|
||||
ack := types.FungibleTokenPacketAcknowledgement{true, ""}
|
||||
ackMsg := channeltypes.NewMsgAcknowledgement(packet, ack.GetBytes(), proof, proofHeight, suite.chainB.SenderAccount.GetAddress())
|
||||
err = suite.coordinator.SendMsgs(suite.chainB, suite.chainA, channelA.ClientID, ackMsg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
ack := types.FungibleTokenPacketAcknowledgement{Success: true}
|
||||
err = suite.coordinator.RelayPacket(suite.chainB, suite.chainA, clientB, clientA, packet, ack.GetBytes())
|
||||
suite.Require().NoError(err) // relay committed
|
||||
|
||||
seq++
|
||||
// NOTE: coins must be explicitly changed in malleate to test invalid cases
|
||||
coins = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
} else {
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}
|
||||
|
||||
// send coins from chainA to chainB
|
||||
@ -264,7 +249,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() {
|
||||
success bool // success of ack
|
||||
}{
|
||||
{"success ack causes no-op", successAck, func() {
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, true},
|
||||
{"successful refund from source chain", failedAck,
|
||||
func() {
|
||||
@ -272,11 +257,11 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() {
|
||||
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, false},
|
||||
{"successful refund from external chain", failedAck,
|
||||
func() {
|
||||
coins = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
}, false, false},
|
||||
}
|
||||
|
||||
@ -339,11 +324,11 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() {
|
||||
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, true},
|
||||
{"successful timeout from external chain",
|
||||
func() {
|
||||
coins = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 100)
|
||||
}, false, true},
|
||||
{"no source prefix on coin denom",
|
||||
func() {
|
||||
@ -351,11 +336,11 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() {
|
||||
}, false, false},
|
||||
{"unescrow failed",
|
||||
func() {
|
||||
coins = ibctesting.NewTransferCoins(channelB, sdk.DefaultBondDenom, 100)
|
||||
coins = types.GetTransferCoin(channelB, sdk.DefaultBondDenom, 100)
|
||||
}, true, false},
|
||||
{"mint failed",
|
||||
func() {
|
||||
coins = ibctesting.NewTransferCoins(channelA, sdk.DefaultBondDenom, 0)
|
||||
coins = types.GetTransferCoin(channelA, sdk.DefaultBondDenom, 0)
|
||||
}, true, false},
|
||||
}
|
||||
|
||||
|
||||
@ -306,7 +306,8 @@ func (am AppModule) OnRecvPacket(
|
||||
types.EventTypePacket,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
|
||||
sdk.NewAttribute(types.AttributeKeyValue, data.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
|
||||
sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)),
|
||||
),
|
||||
)
|
||||
|
||||
@ -338,7 +339,8 @@ func (am AppModule) OnAcknowledgementPacket(
|
||||
types.EventTypePacket,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
|
||||
sdk.NewAttribute(types.AttributeKeyValue, data.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
|
||||
sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)),
|
||||
sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success)),
|
||||
),
|
||||
)
|
||||
@ -375,7 +377,8 @@ func (am AppModule) OnTimeoutPacket(
|
||||
types.EventTypeTimeout,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyRefundValue, data.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom),
|
||||
sdk.NewAttribute(types.AttributeKeyRefundAmount, fmt.Sprintf("%d", data.Amount)),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -3,3 +3,38 @@ order: 4
|
||||
-->
|
||||
|
||||
# Messages
|
||||
|
||||
### MsgTransfer
|
||||
|
||||
A fungible token cross chain transfer is achieved by using the `MsgTransfer`:
|
||||
|
||||
```go
|
||||
type MsgTransfer struct {
|
||||
SourcePort string
|
||||
SourceChannel string
|
||||
Token sdk.Coin
|
||||
Sender sdk.AccAddress
|
||||
Receiver string
|
||||
TimeoutHeight uint64
|
||||
TimeoutTimestamp uint64
|
||||
}
|
||||
```
|
||||
|
||||
This message is expected to fail if:
|
||||
|
||||
- `SourcePort` is invalid (see 24-host naming requirements)
|
||||
- `SourceChannel` is invalid (see 24-host naming requirements)
|
||||
- `Token` is invalid (denom is invalid or amount is negative)
|
||||
- `Token.Amount` is not positive
|
||||
- `Sender` is empty
|
||||
- `Receiver` is empty
|
||||
- `TimeoutHeight` and `TimeoutTimestamp` are both zero
|
||||
|
||||
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
|
||||
`SourcePort` and `SourceChannel`.
|
||||
|
||||
The denomination provided for transfer should correspond to the same denomination
|
||||
represented on this chain. The prefixes will be added as necessary upon by the
|
||||
receiving chain.
|
||||
|
||||
|
||||
@ -19,7 +19,8 @@ order: 5
|
||||
|-----------------------|---------------|-----------------|
|
||||
| fungible_token_packet | module | transfer |
|
||||
| fungible_token_packet | receiver | {receiver} |
|
||||
| fungible_token_packet | value | {amount} |
|
||||
| fungible_token_packet | denom | {denom} |
|
||||
| fungible_token_packet | amount | {amount} |
|
||||
|
||||
## OnAcknowledgePacket callback
|
||||
|
||||
@ -27,7 +28,8 @@ order: 5
|
||||
|-----------------------|---------------|-----------------|
|
||||
| fungible_token_packet | module | transfer |
|
||||
| fungible_token_packet | receiver | {receiver} |
|
||||
| fungible_token_packet | value | {amount} |
|
||||
| fungible_token_packet | denom | {denom} |
|
||||
| fungible_token_packet | amount | {amount} |
|
||||
| fungible_token_packet | success | {ackSuccess} |
|
||||
|
||||
## OnTimeoutPacket callback
|
||||
@ -36,8 +38,8 @@ order: 5
|
||||
|-----------------------|-----------------|-----------------|
|
||||
| fungible_token_packet | module | transfer |
|
||||
| fungible_token_packet | refund_receiver | {receiver} |
|
||||
| fungible_token_packet | value | {amount} |
|
||||
|
||||
| fungible_token_packet | denom | {denom} |
|
||||
| fungible_token_packet | amount | {amount} |
|
||||
|
||||
|
||||
|
||||
|
||||
52
x/ibc-transfer/types/coin.go
Normal file
52
x/ibc-transfer/types/coin.go
Normal file
@ -0,0 +1,52 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// SenderChainIsSource returns false if the denomination originally came
|
||||
// from the receiving chain and true otherwise.
|
||||
func SenderChainIsSource(sourcePort, sourceChannel, denom string) bool {
|
||||
// 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.
|
||||
|
||||
return !ReceiverChainIsSource(sourcePort, sourceChannel, denom)
|
||||
}
|
||||
|
||||
// ReceiverChainIsSource returns true if the denomination originally came
|
||||
// from the receiving chain and false otherwise.
|
||||
func ReceiverChainIsSource(sourcePort, sourceChannel, denom string) bool {
|
||||
// The prefix passed in should contain the SourcePort and SourceChannel.
|
||||
// If the receiver chain originally sent the token to the sender chain
|
||||
// the denom will have the sender's SourcePort and SourceChannel as the
|
||||
// prefix.
|
||||
|
||||
voucherPrefix := GetDenomPrefix(sourcePort, sourceChannel)
|
||||
return strings.HasPrefix(denom, voucherPrefix)
|
||||
|
||||
}
|
||||
|
||||
// GetDenomPrefix returns the receiving denomination prefix
|
||||
func GetDenomPrefix(portID, channelID string) string {
|
||||
return fmt.Sprintf("%s/%s/", portID, channelID)
|
||||
}
|
||||
|
||||
// GetPrefixedDenom returns the denomination with the portID and channelID prefixed
|
||||
func GetPrefixedDenom(portID, channelID, baseDenom string) string {
|
||||
return fmt.Sprintf("%s/%s/%s", portID, channelID, baseDenom)
|
||||
}
|
||||
|
||||
// GetPrefixedCoin creates a copy of the given coin with the prefixed denom
|
||||
func GetPrefixedCoin(portID, channelID string, coin sdk.Coin) sdk.Coin {
|
||||
return sdk.NewCoin(GetPrefixedDenom(portID, channelID, coin.Denom), coin.Amount)
|
||||
}
|
||||
|
||||
// GetTransferCoin creates a transfer coin with the port ID and channel ID
|
||||
// prefixed to the base denom.
|
||||
func GetTransferCoin(portID, channelID, baseDenom string, amount int64) sdk.Coin {
|
||||
return sdk.NewInt64Coin(GetPrefixedDenom(portID, channelID, baseDenom), amount)
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
// IBC channel sentinel errors
|
||||
var (
|
||||
ErrInvalidPacketTimeout = sdkerrors.Register(ModuleName, 2, "invalid packet timeout")
|
||||
ErrOnlyOneDenomAllowed = sdkerrors.Register(ModuleName, 3, "only one denom allowed")
|
||||
ErrInvalidDenomForTransfer = sdkerrors.Register(ModuleName, 4, "invalid denomination for cross-chain transfer")
|
||||
ErrInvalidVersion = sdkerrors.Register(ModuleName, 5, "invalid ICS20 version")
|
||||
ErrInvalidDenomForTransfer = sdkerrors.Register(ModuleName, 3, "invalid denomination for cross-chain transfer")
|
||||
ErrInvalidVersion = sdkerrors.Register(ModuleName, 4, "invalid ICS20 version")
|
||||
ErrInvalidAmount = sdkerrors.Register(ModuleName, 5, "invalid token amount")
|
||||
)
|
||||
|
||||
@ -8,9 +8,11 @@ const (
|
||||
EventTypeChannelClose = "channel_closed"
|
||||
|
||||
AttributeKeyReceiver = "receiver"
|
||||
AttributeKeyValue = "value"
|
||||
AttributeKeyDenom = "denom"
|
||||
AttributeKeyAmount = "amount"
|
||||
AttributeKeyRefundReceiver = "refund_receiver"
|
||||
AttributeKeyRefundValue = "refund_value"
|
||||
AttributeKeyRefundDenom = "refund_denom"
|
||||
AttributeKeyRefundAmount = "refund_amount"
|
||||
AttributeKeyAckSuccess = "success"
|
||||
AttributeKeyAckError = "error"
|
||||
)
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -40,17 +38,3 @@ var PortKey = []byte{0x01}
|
||||
func GetEscrowAddress(portID, channelID string) sdk.AccAddress {
|
||||
return sdk.AccAddress(crypto.AddressHash([]byte(portID + channelID)))
|
||||
}
|
||||
|
||||
// GetDenomPrefix returns the receiving denomination prefix
|
||||
func GetDenomPrefix(portID, channelID string) string {
|
||||
return fmt.Sprintf("%s/%s/", portID, channelID)
|
||||
}
|
||||
|
||||
// GetPrefixedCoins creates a copy of the given coins with the denom updated with the prefix.
|
||||
func GetPrefixedCoins(portID, channelID string, coins ...sdk.Coin) sdk.Coins {
|
||||
prefixedCoins := make(sdk.Coins, len(coins))
|
||||
for i := range coins {
|
||||
prefixedCoins[i] = sdk.NewCoin(GetDenomPrefix(portID, channelID)+coins[i].Denom, coins[i].Amount)
|
||||
}
|
||||
return prefixedCoins
|
||||
}
|
||||
|
||||
@ -14,13 +14,13 @@ const (
|
||||
// NewMsgTransfer creates a new MsgTransfer instance
|
||||
func NewMsgTransfer(
|
||||
sourcePort, sourceChannel string,
|
||||
amount sdk.Coins, sender sdk.AccAddress, receiver string,
|
||||
token sdk.Coin, sender sdk.AccAddress, receiver string,
|
||||
timeoutHeight, timeoutTimestamp uint64,
|
||||
) *MsgTransfer {
|
||||
return &MsgTransfer{
|
||||
SourcePort: sourcePort,
|
||||
SourceChannel: sourceChannel,
|
||||
Amount: amount,
|
||||
Token: token,
|
||||
Sender: sender,
|
||||
Receiver: receiver,
|
||||
TimeoutHeight: timeoutHeight,
|
||||
@ -39,7 +39,7 @@ func (MsgTransfer) Type() string {
|
||||
}
|
||||
|
||||
// ValidateBasic performs a basic check of the MsgTransfer fields.
|
||||
// NOTE: timeout height and timestamp values can be 0 to disable the timeout.
|
||||
// NOTE: timeout height or timestamp values can be 0 to disable the timeout.
|
||||
func (msg MsgTransfer) ValidateBasic() error {
|
||||
if err := host.PortIdentifierValidator(msg.SourcePort); err != nil {
|
||||
return sdkerrors.Wrap(err, "invalid source port ID")
|
||||
@ -47,11 +47,11 @@ func (msg MsgTransfer) ValidateBasic() error {
|
||||
if err := host.ChannelIdentifierValidator(msg.SourceChannel); err != nil {
|
||||
return sdkerrors.Wrap(err, "invalid source channel ID")
|
||||
}
|
||||
if !msg.Amount.IsAllPositive() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, msg.Amount.String())
|
||||
if !msg.Token.IsValid() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Token.String())
|
||||
}
|
||||
if !msg.Amount.IsValid() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
|
||||
if !msg.Token.IsPositive() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, msg.Token.String())
|
||||
}
|
||||
if msg.Sender.Empty() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing sender address")
|
||||
@ -59,7 +59,12 @@ func (msg MsgTransfer) ValidateBasic() error {
|
||||
if msg.Receiver == "" {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing recipient address")
|
||||
}
|
||||
return nil
|
||||
|
||||
// sanity check that validate basic on fungible token packet passes
|
||||
// NOTE: this should always pass since validation checks should be the
|
||||
// same. Please open an issue if you encounter an error on this line.
|
||||
packet := NewFungibleTokenPacketData(msg.Token.Denom, msg.Token.Amount.Uint64(), msg.Sender.String(), msg.Receiver)
|
||||
return packet.ValidateBasic()
|
||||
}
|
||||
|
||||
// GetSignBytes implements sdk.Msg
|
||||
|
||||
@ -27,82 +27,68 @@ var (
|
||||
addr2 = sdk.AccAddress("testaddr2").String()
|
||||
emptyAddr sdk.AccAddress
|
||||
|
||||
coins, _ = sdk.ParseCoins("100atom")
|
||||
invalidDenomCoins = sdk.Coins{sdk.Coin{Denom: "ato-m", Amount: sdk.NewInt(100)}}
|
||||
negativeCoins = sdk.Coins{sdk.Coin{Denom: "atom", Amount: sdk.NewInt(100)}, sdk.Coin{Denom: "atoms", Amount: sdk.NewInt(-100)}}
|
||||
coin = sdk.NewCoin("atom", sdk.NewInt(100))
|
||||
invalidDenomCoin = sdk.Coin{Denom: "ato-m", Amount: sdk.NewInt(100)}
|
||||
negativeCoin = sdk.Coin{Denom: "atoms", Amount: sdk.NewInt(-100)}
|
||||
)
|
||||
|
||||
// TestMsgTransferRoute tests Route for MsgTransfer
|
||||
func TestMsgTransferRoute(t *testing.T) {
|
||||
msg := NewMsgTransfer(validPort, validChannel, coins, addr1, addr2, 10, 0)
|
||||
msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, 10, 0)
|
||||
|
||||
require.Equal(t, RouterKey, msg.Route())
|
||||
}
|
||||
|
||||
// TestMsgTransferType tests Type for MsgTransfer
|
||||
func TestMsgTransferType(t *testing.T) {
|
||||
msg := NewMsgTransfer(validPort, validChannel, coins, addr1, addr2, 10, 0)
|
||||
msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, 10, 0)
|
||||
|
||||
require.Equal(t, "transfer", msg.Type())
|
||||
}
|
||||
|
||||
// TestMsgTransferValidation tests ValidateBasic for MsgTransfer
|
||||
func TestMsgTransferValidation(t *testing.T) {
|
||||
testMsgs := []*MsgTransfer{
|
||||
NewMsgTransfer(validPort, validChannel, coins, addr1, addr2, 10, 0), // valid msg
|
||||
NewMsgTransfer(invalidShortPort, validChannel, coins, addr1, addr2, 10, 0), // too short port id
|
||||
NewMsgTransfer(invalidLongPort, validChannel, coins, addr1, addr2, 10, 0), // too long port id
|
||||
NewMsgTransfer(invalidPort, validChannel, coins, addr1, addr2, 10, 0), // port id contains non-alpha
|
||||
NewMsgTransfer(validPort, invalidShortChannel, coins, addr1, addr2, 10, 0), // too short channel id
|
||||
NewMsgTransfer(validPort, invalidLongChannel, coins, addr1, addr2, 10, 0), // too long channel id
|
||||
NewMsgTransfer(validPort, invalidChannel, coins, addr1, addr2, 10, 0), // channel id contains non-alpha
|
||||
NewMsgTransfer(validPort, validChannel, invalidDenomCoins, addr1, addr2, 10, 0), // invalid amount
|
||||
NewMsgTransfer(validPort, validChannel, negativeCoins, addr1, addr2, 10, 0), // amount contains negative coin
|
||||
NewMsgTransfer(validPort, validChannel, coins, emptyAddr, addr2, 10, 0), // missing sender address
|
||||
NewMsgTransfer(validPort, validChannel, coins, addr1, "", 10, 0), // missing recipient address
|
||||
NewMsgTransfer(validPort, validChannel, sdk.Coins{}, addr1, addr2, 10, 0), // not possitive coin
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
msg *MsgTransfer
|
||||
expPass bool
|
||||
errMsg string
|
||||
}{
|
||||
{testMsgs[0], true, ""},
|
||||
{testMsgs[1], false, "too short port id"},
|
||||
{testMsgs[2], false, "too long port id"},
|
||||
{testMsgs[3], false, "port id contains non-alpha"},
|
||||
{testMsgs[4], false, "too short channel id"},
|
||||
{testMsgs[5], false, "too long channel id"},
|
||||
{testMsgs[6], false, "channel id contains non-alpha"},
|
||||
{testMsgs[7], false, "invalid amount"},
|
||||
{testMsgs[8], false, "amount contains negative coin"},
|
||||
{testMsgs[9], false, "missing sender address"},
|
||||
{testMsgs[10], false, "missing recipient address"},
|
||||
{"valid msg", NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, 10, 0), true},
|
||||
{"too short port id", NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"too long port id", NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"port id contains non-alpha", NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"too short channel id", NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"too long channel id", NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"channel id contains non-alpha", NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, 10, 0), false},
|
||||
{"invalid amount", NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, 10, 0), false},
|
||||
{"negative coin", NewMsgTransfer(validPort, validChannel, negativeCoin, addr1, addr2, 10, 0), false},
|
||||
{"missing sender address", NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, 10, 0), false},
|
||||
{"missing recipient address", NewMsgTransfer(validPort, validChannel, coin, addr1, "", 10, 0), false},
|
||||
{"empty coin", NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, 10, 0), false},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
err := tc.msg.ValidateBasic()
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, "Msg %d failed: %v", i, err)
|
||||
require.NoError(t, err, "valid test case %d failed: %v", i, err)
|
||||
} else {
|
||||
require.Error(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg)
|
||||
require.Error(t, err, "invalid test case %d passed: %s", i, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgTransferGetSignBytes tests GetSignBytes for MsgTransfer
|
||||
func TestMsgTransferGetSignBytes(t *testing.T) {
|
||||
msg := NewMsgTransfer(validPort, validChannel, coins, addr1, addr2, 110, 10)
|
||||
msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, 110, 10)
|
||||
res := msg.GetSignBytes()
|
||||
|
||||
expected := `{"type":"cosmos-sdk/MsgTransfer","value":{"amount":[{"amount":"100","denom":"atom"}],"receiver":"cosmos1w3jhxarpv3j8yvs7f9y7g","sender":"cosmos1w3jhxarpv3j8yvg4ufs4x","source_channel":"testchannel","source_port":"testportid","timeout_height":"110","timeout_timestamp":"10"}}`
|
||||
expected := `{"type":"cosmos-sdk/MsgTransfer","value":{"receiver":"cosmos1w3jhxarpv3j8yvs7f9y7g","sender":"cosmos1w3jhxarpv3j8yvg4ufs4x","source_channel":"testchannel","source_port":"testportid","timeout_height":"110","timeout_timestamp":"10","token":{"amount":"100","denom":"atom"}}}`
|
||||
require.Equal(t, expected, string(res))
|
||||
}
|
||||
|
||||
// TestMsgTransferGetSigners tests GetSigners for MsgTransfer
|
||||
func TestMsgTransferGetSigners(t *testing.T) {
|
||||
msg := NewMsgTransfer(validPort, validChannel, coins, addr1, addr2, 10, 0)
|
||||
msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, 10, 0)
|
||||
res := msg.GetSigners()
|
||||
|
||||
expected := "[746573746164647231]"
|
||||
|
||||
@ -23,8 +23,11 @@ var (
|
||||
|
||||
// NewFungibleTokenPacketData contructs a new FungibleTokenPacketData instance
|
||||
func NewFungibleTokenPacketData(
|
||||
amount sdk.Coins, sender, receiver string) FungibleTokenPacketData {
|
||||
denom string, amount uint64,
|
||||
sender, receiver string,
|
||||
) FungibleTokenPacketData {
|
||||
return FungibleTokenPacketData{
|
||||
Denom: denom,
|
||||
Amount: amount,
|
||||
Sender: sender,
|
||||
Receiver: receiver,
|
||||
@ -33,11 +36,11 @@ func NewFungibleTokenPacketData(
|
||||
|
||||
// ValidateBasic is used for validating the token transfer
|
||||
func (ftpd FungibleTokenPacketData) ValidateBasic() error {
|
||||
if !ftpd.Amount.IsAllPositive() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, ftpd.Amount.String())
|
||||
if strings.TrimSpace(ftpd.Denom) == "" {
|
||||
return sdkerrors.Wrap(ErrInvalidDenomForTransfer, "denom cannot be empty")
|
||||
}
|
||||
if !ftpd.Amount.IsValid() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, ftpd.Amount.String())
|
||||
if ftpd.Amount == 0 {
|
||||
return sdkerrors.Wrap(ErrInvalidAmount, "amount cannot be 0")
|
||||
}
|
||||
if strings.TrimSpace(ftpd.Sender) == "" {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be blank")
|
||||
|
||||
@ -6,34 +6,31 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
denom = "transfer/gaiachannel/atom"
|
||||
amount = uint64(100)
|
||||
)
|
||||
|
||||
// TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData
|
||||
func TestFungibleTokenPacketDataValidateBasic(t *testing.T) {
|
||||
testPacketDataTransfer := []FungibleTokenPacketData{
|
||||
NewFungibleTokenPacketData(coins, addr1.String(), addr2), // valid msg
|
||||
NewFungibleTokenPacketData(invalidDenomCoins, addr1.String(), addr2), // invalid amount
|
||||
NewFungibleTokenPacketData(negativeCoins, addr1.String(), addr2), // amount contains negative coin
|
||||
NewFungibleTokenPacketData(coins, emptyAddr.String(), addr2), // missing sender address
|
||||
NewFungibleTokenPacketData(coins, addr1.String(), emptyAddr.String()), // missing recipient address
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
packetData FungibleTokenPacketData
|
||||
expPass bool
|
||||
errMsg string
|
||||
}{
|
||||
{testPacketDataTransfer[0], true, ""},
|
||||
{testPacketDataTransfer[1], false, "invalid amount"},
|
||||
{testPacketDataTransfer[2], false, "amount contains negative coin"},
|
||||
{testPacketDataTransfer[3], false, "missing sender address"},
|
||||
{testPacketDataTransfer[4], false, "missing recipient address"},
|
||||
{"valid packet", NewFungibleTokenPacketData(denom, amount, addr1.String(), addr2), true},
|
||||
{"invalid denom", NewFungibleTokenPacketData("", amount, addr1.String(), addr2), false},
|
||||
{"invalid amount", NewFungibleTokenPacketData(denom, 0, addr1.String(), addr2), false},
|
||||
{"missing sender address", NewFungibleTokenPacketData(denom, amount, emptyAddr.String(), addr2), false},
|
||||
{"missing recipient address", NewFungibleTokenPacketData(denom, amount, addr1.String(), emptyAddr.String()), false},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
err := tc.packetData.ValidateBasic()
|
||||
if tc.expPass {
|
||||
require.NoError(t, err, "PacketDataTransfer %d failed: %v", i, err)
|
||||
require.NoError(t, err, "valid test case %d failed: %v", i, err)
|
||||
} else {
|
||||
require.Error(t, err, "Invalid PacketDataTransfer %d passed: %s", i, tc.errMsg)
|
||||
require.Error(t, err, "invalid test case %d passed: %s", i, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,15 +25,16 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between ICS20 enabled chains.
|
||||
// See ICS Spec here: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between
|
||||
// ICS20 enabled chains. See ICS Spec here:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
type MsgTransfer struct {
|
||||
// the port on which the packet will be sent
|
||||
SourcePort string `protobuf:"bytes,1,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"`
|
||||
// the channel by which the packet will be sent
|
||||
SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"`
|
||||
// the tokens to be transferred
|
||||
Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"`
|
||||
Token types.Coin `protobuf:"bytes,3,opt,name=token,proto3" json:"token"`
|
||||
// the sender address
|
||||
Sender github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,4,opt,name=sender,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"sender,omitempty"`
|
||||
// the recipient address on the destination chain
|
||||
@ -93,11 +94,11 @@ func (m *MsgTransfer) GetSourceChannel() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MsgTransfer) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins {
|
||||
func (m *MsgTransfer) GetToken() types.Coin {
|
||||
if m != nil {
|
||||
return m.Amount
|
||||
return m.Token
|
||||
}
|
||||
return nil
|
||||
return types.Coin{}
|
||||
}
|
||||
|
||||
func (m *MsgTransfer) GetSender() github_com_cosmos_cosmos_sdk_types.AccAddress {
|
||||
@ -129,14 +130,17 @@ func (m *MsgTransfer) GetTimeoutTimestamp() uint64 {
|
||||
}
|
||||
|
||||
// FungibleTokenPacketData defines a struct for the packet payload
|
||||
// See FungibleTokenPacketData spec: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
// See FungibleTokenPacketData spec:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures
|
||||
type FungibleTokenPacketData struct {
|
||||
// the tokens to be transferred
|
||||
Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"`
|
||||
// the token denomination to be transferred
|
||||
Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"`
|
||||
// the token amount to be transferred
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
// the sender address
|
||||
Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"`
|
||||
Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"`
|
||||
// the recipient address on the destination chain
|
||||
Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"`
|
||||
Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} }
|
||||
@ -172,11 +176,18 @@ func (m *FungibleTokenPacketData) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_FungibleTokenPacketData proto.InternalMessageInfo
|
||||
|
||||
func (m *FungibleTokenPacketData) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins {
|
||||
func (m *FungibleTokenPacketData) GetDenom() string {
|
||||
if m != nil {
|
||||
return m.Denom
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *FungibleTokenPacketData) GetAmount() uint64 {
|
||||
if m != nil {
|
||||
return m.Amount
|
||||
}
|
||||
return nil
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *FungibleTokenPacketData) GetSender() string {
|
||||
@ -193,9 +204,10 @@ func (m *FungibleTokenPacketData) GetReceiver() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FungibleTokenPacketAcknowledgement contains a boolean success flag and an optional error msg
|
||||
// error msg is empty string on success
|
||||
// See spec for onAcknowledgePacket: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
// FungibleTokenPacketAcknowledgement contains a boolean success flag and an
|
||||
// optional error msg error msg is empty string on success See spec for
|
||||
// onAcknowledgePacket:
|
||||
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
|
||||
type FungibleTokenPacketAcknowledgement struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
|
||||
@ -257,38 +269,38 @@ func init() {
|
||||
func init() { proto.RegisterFile("ibc/transfer/transfer.proto", fileDescriptor_08134a70fd29e656) }
|
||||
|
||||
var fileDescriptor_08134a70fd29e656 = []byte{
|
||||
// 484 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x3f, 0x6f, 0xd3, 0x40,
|
||||
0x1c, 0x8d, 0x49, 0x9b, 0x96, 0x4b, 0x8a, 0xe0, 0x28, 0xe5, 0x08, 0xc8, 0x8e, 0x3c, 0x79, 0x49,
|
||||
0x4c, 0x61, 0x40, 0x62, 0x22, 0x29, 0x42, 0x54, 0x08, 0xa9, 0xb2, 0x22, 0x06, 0x96, 0xca, 0x3e,
|
||||
0xff, 0x70, 0x4e, 0x89, 0xef, 0xa2, 0xbb, 0x33, 0xd0, 0x6f, 0xc1, 0x97, 0x60, 0xe1, 0x93, 0x74,
|
||||
0xec, 0xc0, 0xc0, 0x64, 0x50, 0xf2, 0x0d, 0x32, 0x32, 0x21, 0xff, 0x89, 0x89, 0xa5, 0x08, 0x31,
|
||||
0x74, 0xba, 0x7b, 0xef, 0xfd, 0x7e, 0xcf, 0xf7, 0x7b, 0xbe, 0x43, 0x0f, 0x59, 0x40, 0x5d, 0x2d,
|
||||
0x7d, 0xae, 0x3e, 0x80, 0xac, 0x36, 0x83, 0xb9, 0x14, 0x5a, 0xe0, 0x0e, 0x0b, 0xe8, 0x60, 0xcd,
|
||||
0x75, 0x0f, 0x23, 0x11, 0x89, 0x5c, 0x70, 0xb3, 0x5d, 0x51, 0xd3, 0xbd, 0x4b, 0x85, 0x8a, 0x85,
|
||||
0x72, 0x8b, 0xa5, 0x20, 0xed, 0xef, 0x4d, 0xd4, 0x7e, 0xab, 0xa2, 0x71, 0xd9, 0x8a, 0x9f, 0xa1,
|
||||
0xb6, 0x12, 0x89, 0xa4, 0x70, 0x3e, 0x17, 0x52, 0x13, 0xa3, 0x67, 0x38, 0x37, 0x47, 0x47, 0xab,
|
||||
0xd4, 0xc2, 0x17, 0x7e, 0x3c, 0x7b, 0x6e, 0x6f, 0x88, 0xb6, 0x87, 0x0a, 0x74, 0x26, 0xa4, 0xc6,
|
||||
0x2f, 0xd0, 0xad, 0x52, 0xa3, 0x13, 0x9f, 0x73, 0x98, 0x91, 0x1b, 0x79, 0xef, 0x83, 0x55, 0x6a,
|
||||
0xdd, 0xab, 0xf5, 0x96, 0xba, 0xed, 0x1d, 0x14, 0xc4, 0x49, 0x81, 0xf1, 0x3b, 0xd4, 0xf2, 0x63,
|
||||
0x91, 0x70, 0x4d, 0x9a, 0xbd, 0xa6, 0xd3, 0x7e, 0xd2, 0x19, 0x94, 0x27, 0x3d, 0x11, 0x8c, 0x8f,
|
||||
0x1e, 0x5f, 0xa6, 0x56, 0xe3, 0xdb, 0x4f, 0xcb, 0x89, 0x98, 0x9e, 0x24, 0xc1, 0x80, 0x8a, 0xd8,
|
||||
0xad, 0x0d, 0xd4, 0x57, 0xe1, 0xd4, 0xd5, 0x17, 0x73, 0x28, 0x1a, 0x94, 0x57, 0xba, 0xe1, 0x53,
|
||||
0xd4, 0x52, 0xc0, 0x43, 0x90, 0x64, 0xa7, 0x67, 0x38, 0x9d, 0xd1, 0xf1, 0xef, 0xd4, 0xea, 0xff,
|
||||
0x87, 0xcb, 0x90, 0xd2, 0x61, 0x18, 0x4a, 0x50, 0xca, 0x2b, 0x0d, 0x70, 0x17, 0xed, 0x4b, 0xa0,
|
||||
0xc0, 0x3e, 0x82, 0x24, 0xbb, 0xd9, 0x78, 0x5e, 0x85, 0xb3, 0x00, 0x34, 0x8b, 0x41, 0x24, 0xfa,
|
||||
0x7c, 0x02, 0x2c, 0x9a, 0x68, 0xd2, 0xea, 0x19, 0xce, 0xce, 0x66, 0x00, 0x75, 0xdd, 0xf6, 0x0e,
|
||||
0x4a, 0xe2, 0x75, 0x8e, 0xf1, 0x29, 0xba, 0xb3, 0xae, 0xc8, 0x56, 0xa5, 0xfd, 0x78, 0x4e, 0xf6,
|
||||
0x72, 0x93, 0x47, 0xab, 0xd4, 0x22, 0x75, 0x93, 0xaa, 0xc4, 0xf6, 0x6e, 0x97, 0xdc, 0xb8, 0xa2,
|
||||
0xbe, 0x1a, 0xe8, 0xfe, 0xab, 0x84, 0x47, 0x2c, 0x98, 0xc1, 0x58, 0x4c, 0x81, 0x9f, 0xf9, 0x74,
|
||||
0x0a, 0xfa, 0xa5, 0xaf, 0xfd, 0x8d, 0x9c, 0x8d, 0x6b, 0xcd, 0xf9, 0xa8, 0xca, 0x39, 0xff, 0xf3,
|
||||
0x5b, 0x43, 0x6b, 0xd6, 0x43, 0xb3, 0xc7, 0xc8, 0xde, 0x72, 0xcc, 0x21, 0x9d, 0x72, 0xf1, 0x69,
|
||||
0x06, 0x61, 0x04, 0x31, 0x70, 0x8d, 0x09, 0xda, 0x53, 0x09, 0xa5, 0xa0, 0x54, 0x7e, 0x21, 0xf7,
|
||||
0xbd, 0x35, 0xc4, 0x87, 0x68, 0x17, 0xa4, 0x14, 0xeb, 0x4f, 0x16, 0x60, 0xf4, 0xe6, 0x72, 0x61,
|
||||
0x1a, 0x57, 0x0b, 0xd3, 0xf8, 0xb5, 0x30, 0x8d, 0x2f, 0x4b, 0xb3, 0x71, 0xb5, 0x34, 0x1b, 0x3f,
|
||||
0x96, 0x66, 0xe3, 0xfd, 0xf1, 0x3f, 0xa7, 0xfa, 0xec, 0xb2, 0x80, 0xf6, 0xff, 0xbe, 0xb1, 0x6c,
|
||||
0xc8, 0xa0, 0x95, 0x3f, 0x94, 0xa7, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6a, 0x95, 0x43, 0xad,
|
||||
0x80, 0x03, 0x00, 0x00,
|
||||
// 485 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xcf, 0x6e, 0xd3, 0x30,
|
||||
0x18, 0x6f, 0x68, 0xda, 0x0d, 0xb7, 0x43, 0x60, 0xc6, 0x08, 0x05, 0x25, 0x55, 0x4e, 0xb9, 0x34,
|
||||
0xd1, 0xe0, 0x80, 0xc4, 0x89, 0x76, 0x08, 0x31, 0x21, 0xa4, 0xc9, 0xea, 0x89, 0xcb, 0x94, 0x38,
|
||||
0x1f, 0x69, 0xd4, 0xc6, 0xae, 0x6c, 0x07, 0x98, 0x78, 0x09, 0x9e, 0x82, 0x67, 0xd9, 0x71, 0x47,
|
||||
0x4e, 0x11, 0x6a, 0xdf, 0xa0, 0x47, 0x4e, 0x28, 0x71, 0x5a, 0x16, 0xa9, 0xda, 0xc9, 0xfe, 0xfd,
|
||||
0xf9, 0xbe, 0x7c, 0x7f, 0x1c, 0xf4, 0x3c, 0x8d, 0x68, 0xa0, 0x44, 0xc8, 0xe4, 0x17, 0x10, 0xbb,
|
||||
0x8b, 0xbf, 0x14, 0x5c, 0x71, 0xdc, 0x4f, 0x23, 0xea, 0x6f, 0xb9, 0xc1, 0x71, 0xc2, 0x13, 0x5e,
|
||||
0x09, 0x41, 0x79, 0xd3, 0x9e, 0xc1, 0x63, 0xca, 0x65, 0xc6, 0x65, 0xa0, 0x0f, 0x4d, 0xba, 0xbf,
|
||||
0xda, 0xa8, 0xf7, 0x49, 0x26, 0xd3, 0x3a, 0x14, 0xbf, 0x46, 0x3d, 0xc9, 0x73, 0x41, 0xe1, 0x72,
|
||||
0xc9, 0x85, 0xb2, 0x8c, 0xa1, 0xe1, 0xdd, 0x9f, 0x9c, 0x6c, 0x0a, 0x07, 0x5f, 0x85, 0xd9, 0xe2,
|
||||
0x8d, 0x7b, 0x4b, 0x74, 0x09, 0xd2, 0xe8, 0x82, 0x0b, 0x85, 0xdf, 0xa2, 0x07, 0xb5, 0x46, 0x67,
|
||||
0x21, 0x63, 0xb0, 0xb0, 0xee, 0x55, 0xb1, 0xcf, 0x36, 0x85, 0xf3, 0xa4, 0x11, 0x5b, 0xeb, 0x2e,
|
||||
0x39, 0xd2, 0xc4, 0x99, 0xc6, 0xd8, 0x43, 0x1d, 0xc5, 0xe7, 0xc0, 0xac, 0xf6, 0xd0, 0xf0, 0x7a,
|
||||
0x2f, 0xfb, 0x7e, 0x5d, 0xe8, 0x19, 0x4f, 0xd9, 0xc4, 0xbc, 0x2e, 0x9c, 0x16, 0xd1, 0x06, 0x7c,
|
||||
0x8e, 0xba, 0x12, 0x58, 0x0c, 0xc2, 0x32, 0x87, 0x86, 0xd7, 0x9f, 0x9c, 0xfe, 0x2d, 0x9c, 0x51,
|
||||
0x92, 0xaa, 0x59, 0x1e, 0xf9, 0x94, 0x67, 0x41, 0xa3, 0xd1, 0x91, 0x8c, 0xe7, 0x81, 0xba, 0x5a,
|
||||
0x82, 0xf4, 0xc7, 0x94, 0x8e, 0xe3, 0x58, 0x80, 0x94, 0xa4, 0x4e, 0x80, 0x07, 0xe8, 0x50, 0x00,
|
||||
0x85, 0xf4, 0x2b, 0x08, 0xab, 0x53, 0x16, 0x4c, 0x76, 0xb8, 0x6c, 0x49, 0xa5, 0x19, 0xf0, 0x5c,
|
||||
0x5d, 0xce, 0x20, 0x4d, 0x66, 0xca, 0xea, 0x0e, 0x0d, 0xcf, 0xbc, 0xdd, 0x52, 0x53, 0x77, 0xc9,
|
||||
0x51, 0x4d, 0x7c, 0xa8, 0x30, 0x3e, 0x47, 0x8f, 0xb6, 0x8e, 0xf2, 0x94, 0x2a, 0xcc, 0x96, 0xd6,
|
||||
0x41, 0x95, 0xe4, 0xc5, 0xa6, 0x70, 0xac, 0x66, 0x92, 0x9d, 0xc5, 0x25, 0x0f, 0x6b, 0x6e, 0xba,
|
||||
0xa3, 0x7e, 0xa0, 0xa7, 0xef, 0x73, 0x96, 0xa4, 0xd1, 0x02, 0xa6, 0xe5, 0x10, 0x2e, 0x42, 0x3a,
|
||||
0x07, 0xf5, 0x2e, 0x54, 0x21, 0x3e, 0x46, 0x9d, 0x18, 0x18, 0xcf, 0xf4, 0xb6, 0x88, 0x06, 0xf8,
|
||||
0x04, 0x75, 0xc3, 0x8c, 0xe7, 0x4c, 0x55, 0x8b, 0x30, 0x49, 0x8d, 0x4a, 0xbe, 0x1e, 0x5e, 0xbb,
|
||||
0xb2, 0xef, 0x9b, 0x84, 0xd9, 0x9c, 0x84, 0x3b, 0x45, 0xee, 0x9e, 0x8f, 0x8f, 0xe9, 0x9c, 0xf1,
|
||||
0x6f, 0x0b, 0x88, 0x13, 0xc8, 0x80, 0x29, 0x6c, 0xa1, 0x03, 0x99, 0x53, 0x0a, 0x52, 0x56, 0x95,
|
||||
0x1c, 0x92, 0x2d, 0x2c, 0x2b, 0x04, 0x21, 0xb8, 0xd0, 0x6f, 0x82, 0x68, 0x30, 0xf9, 0x78, 0xbd,
|
||||
0xb2, 0x8d, 0x9b, 0x95, 0x6d, 0xfc, 0x59, 0xd9, 0xc6, 0xcf, 0xb5, 0xdd, 0xba, 0x59, 0xdb, 0xad,
|
||||
0xdf, 0x6b, 0xbb, 0xf5, 0xf9, 0xf4, 0xce, 0x65, 0x7e, 0x0f, 0xd2, 0x88, 0x8e, 0xfe, 0xff, 0x0a,
|
||||
0xe5, 0x6e, 0xa3, 0x6e, 0xf5, 0x9e, 0x5f, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xc4, 0xe2,
|
||||
0x00, 0x27, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *MsgTransfer) Marshal() (dAtA []byte, err error) {
|
||||
@ -335,20 +347,16 @@ func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if len(m.Amount) > 0 {
|
||||
for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
{
|
||||
size, err := m.Token.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
if len(m.SourceChannel) > 0 {
|
||||
i -= len(m.SourceChannel)
|
||||
copy(dAtA[i:], m.SourceChannel)
|
||||
@ -391,28 +399,26 @@ func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error)
|
||||
copy(dAtA[i:], m.Receiver)
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(len(m.Receiver)))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if len(m.Sender) > 0 {
|
||||
i -= len(m.Sender)
|
||||
copy(dAtA[i:], m.Sender)
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(len(m.Sender)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if len(m.Amount) > 0 {
|
||||
for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
if m.Amount != 0 {
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(m.Amount))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
if len(m.Denom) > 0 {
|
||||
i -= len(m.Denom)
|
||||
copy(dAtA[i:], m.Denom)
|
||||
i = encodeVarintTransfer(dAtA, i, uint64(len(m.Denom)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
@ -482,12 +488,8 @@ func (m *MsgTransfer) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
}
|
||||
if len(m.Amount) > 0 {
|
||||
for _, e := range m.Amount {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
}
|
||||
}
|
||||
l = m.Token.Size()
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
l = len(m.Sender)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
@ -511,11 +513,12 @@ func (m *FungibleTokenPacketData) Size() (n int) {
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Amount) > 0 {
|
||||
for _, e := range m.Amount {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
}
|
||||
l = len(m.Denom)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTransfer(uint64(l))
|
||||
}
|
||||
if m.Amount != 0 {
|
||||
n += 1 + sovTransfer(uint64(m.Amount))
|
||||
}
|
||||
l = len(m.Sender)
|
||||
if l > 0 {
|
||||
@ -645,7 +648,7 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error {
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@ -672,8 +675,7 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error {
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Amount = append(m.Amount, types.Coin{})
|
||||
if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
if err := m.Token.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
@ -836,9 +838,9 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error {
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
|
||||
}
|
||||
var msglen int
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTransfer
|
||||
@ -848,27 +850,44 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthTransfer
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTransfer
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Amount = append(m.Amount, types.Coin{})
|
||||
if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
m.Denom = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
|
||||
}
|
||||
m.Amount = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTransfer
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Amount |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
|
||||
}
|
||||
@ -900,7 +919,7 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Sender = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType)
|
||||
}
|
||||
|
||||
@ -326,8 +326,11 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
|
||||
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
|
||||
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
|
||||
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
|
||||
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// send from chainA to chainB
|
||||
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
|
||||
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
// need to update chainA client to prove missing ack
|
||||
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
|
||||
@ -337,11 +340,15 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
|
||||
{"success: UNORDERED", func() {
|
||||
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
|
||||
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
|
||||
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// send from chainA to chainB
|
||||
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
|
||||
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
|
||||
// need to update chainA client to prove missing ack
|
||||
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
|
||||
|
||||
packetKey = host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
|
||||
}, true},
|
||||
{"success: UNORDERED timeout out of order packet", func() {
|
||||
@ -352,8 +359,10 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
|
||||
for i := uint64(1); i < 10; i++ {
|
||||
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
|
||||
|
||||
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
|
||||
suite.Require().NoError(err)
|
||||
// send from chainA to chainB
|
||||
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
|
||||
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
}
|
||||
// need to update chainA client to prove missing ack
|
||||
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
|
||||
@ -368,8 +377,10 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() {
|
||||
for i := uint64(1); i < 10; i++ {
|
||||
packet = channeltypes.NewPacket(suite.chainA.GetPacketData(suite.chainB), i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), 0)
|
||||
|
||||
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
|
||||
suite.Require().NoError(err)
|
||||
// send from chainA to chainB
|
||||
msg := ibctransfertypes.NewMsgTransfer(channelA.PortID, channelA.ID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), packet.GetTimeoutHeight(), packet.GetTimeoutTimestamp())
|
||||
err := suite.coordinator.SendMsgs(suite.chainA, suite.chainB, clientB, msg)
|
||||
suite.Require().NoError(err) // message committed
|
||||
}
|
||||
// need to update chainA client to prove missing ack
|
||||
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
|
||||
|
||||
@ -54,6 +54,7 @@ const (
|
||||
var (
|
||||
DefaultTrustLevel tmmath.Fraction = lite.DefaultTrustLevel
|
||||
TestHash = []byte("TESTING HASH")
|
||||
TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
|
||||
|
||||
ConnectionVersion = connectiontypes.GetCompatibleEncodedVersions()[0]
|
||||
)
|
||||
@ -594,9 +595,14 @@ func (chain *TestChain) ChanCloseInit(
|
||||
}
|
||||
|
||||
// GetPacketData returns a ibc-transfer marshalled packet to be used for
|
||||
// callback testing
|
||||
// callback testing.
|
||||
func (chain *TestChain) GetPacketData(counterparty *TestChain) []byte {
|
||||
packet := ibctransfertypes.NewFungibleTokenPacketData(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), chain.SenderAccount.GetAddress().String(), counterparty.SenderAccount.GetAddress().String())
|
||||
packet := ibctransfertypes.FungibleTokenPacketData{
|
||||
Denom: TestCoin.Denom,
|
||||
Amount: TestCoin.Amount.Uint64(),
|
||||
Sender: chain.SenderAccount.GetAddress().String(),
|
||||
Receiver: counterparty.SenderAccount.GetAddress().String(),
|
||||
}
|
||||
|
||||
return packet.GetBytes()
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
|
||||
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
||||
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -244,6 +245,55 @@ func (coord *Coordinator) AcknowledgementExecuted(
|
||||
)
|
||||
}
|
||||
|
||||
// RecvPacket receives a channel packet on the counterparty chain and updates
|
||||
// the client on the source chain representing the counterparty.
|
||||
func (coord *Coordinator) RecvPacket(
|
||||
source, counterparty *TestChain,
|
||||
sourceClient string,
|
||||
packet channeltypes.Packet,
|
||||
) error {
|
||||
// get proof of packet commitment on source
|
||||
packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
|
||||
proof, proofHeight := source.QueryProof(packetKey)
|
||||
|
||||
recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, counterparty.SenderAccount.GetAddress())
|
||||
|
||||
// receive on counterparty and update source client
|
||||
return coord.SendMsgs(counterparty, source, sourceClient, recvMsg)
|
||||
}
|
||||
|
||||
// AcknowledgePacket acknowledges on the source chain the packet received on
|
||||
// the counterparty chain and updates the client on the counterparty representing
|
||||
// the source chain.
|
||||
// TODO: add a query for the acknowledgement by events
|
||||
// - https://github.com/cosmos/cosmos-sdk/issues/6509
|
||||
func (coord *Coordinator) AcknowledgePacket(
|
||||
source, counterparty *TestChain,
|
||||
counterpartyClient string,
|
||||
packet channeltypes.Packet, ack []byte,
|
||||
) error {
|
||||
// get proof of acknowledgement on counterparty
|
||||
packetKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
|
||||
proof, proofHeight := counterparty.QueryProof(packetKey)
|
||||
|
||||
ackMsg := channeltypes.NewMsgAcknowledgement(packet, ack, proof, proofHeight, source.SenderAccount.GetAddress())
|
||||
return coord.SendMsgs(source, counterparty, counterpartyClient, ackMsg)
|
||||
}
|
||||
|
||||
// RelayPacket receives a channel packet on counterparty, queries the ack
|
||||
// and acknowledges the packet on source. The clients are updated as needed.
|
||||
func (coord *Coordinator) RelayPacket(
|
||||
source, counterparty *TestChain,
|
||||
sourceClient, counterpartyClient string,
|
||||
packet channeltypes.Packet, ack []byte,
|
||||
) error {
|
||||
if err := coord.RecvPacket(source, counterparty, sourceClient, packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return coord.AcknowledgePacket(source, counterparty, counterpartyClient, packet, ack)
|
||||
}
|
||||
|
||||
// IncrementTime iterates through all the TestChain's and increments their current header time
|
||||
// by 5 seconds.
|
||||
//
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func NewTransferCoins(dst TestChannel, denom string, amount int64) sdk.Coins {
|
||||
return sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%s/%s/%s", dst.PortID, dst.ID, denom), sdk.NewInt(amount)))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user