diff --git a/simapp/app.go b/simapp/app.go index 750472533b57..347e32a687c2 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -23,6 +23,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/ibc" + ibctransfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/params" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" @@ -60,12 +61,13 @@ var ( // module account permissions maccPerms = map[string][]string{ - auth.FeeCollectorName: nil, - distr.ModuleName: nil, - mint.ModuleName: {supply.Minter}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - gov.ModuleName: {supply.Burner}, + auth.FeeCollectorName: nil, + distr.ModuleName: nil, + mint.ModuleName: {supply.Minter}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + gov.ModuleName: {supply.Burner}, + ibctransfer.GetModuleAccountName(): {supply.Minter, supply.Burner}, } ) @@ -175,7 +177,7 @@ func NewSimApp( staking.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), ) - app.IBCKeeper = ibc.NewKeeper(app.cdc, keys[ibc.StoreKey], ibc.DefaultCodespace) + app.IBCKeeper = ibc.NewKeeper(app.cdc, keys[ibc.StoreKey], ibc.DefaultCodespace, app.BankKeeper, app.SupplyKeeper) // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. diff --git a/x/ibc/04-channel/types/codec.go b/x/ibc/04-channel/types/codec.go index 4cfc1e773a1e..d3271d9c5610 100644 --- a/x/ibc/04-channel/types/codec.go +++ b/x/ibc/04-channel/types/codec.go @@ -9,6 +9,7 @@ var SubModuleCdc *codec.Codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.PacketI)(nil), nil) + cdc.RegisterConcrete(Packet{}, "ibc/channel/Packet", nil) cdc.RegisterConcrete(OpaquePacket{}, "ibc/channel/OpaquePacket", nil) cdc.RegisterConcrete(MsgChannelOpenInit{}, "ibc/channel/MsgChannelOpenInit", nil) diff --git a/x/ibc/04-channel/types/events.go b/x/ibc/04-channel/types/events.go index 72d92cbf2237..b82b3e8700cf 100644 --- a/x/ibc/04-channel/types/events.go +++ b/x/ibc/04-channel/types/events.go @@ -12,7 +12,6 @@ const ( AttributeKeyReceiverPort = "receiver_port" AttributeKeyChannelID = "channel_id" AttributeKeySequence = "sequence" - AttributeKeyPacket = "packet" ) // IBC channel events vars diff --git a/x/ibc/20-transfer/alias.go b/x/ibc/20-transfer/alias.go new file mode 100644 index 000000000000..ba4008d57374 --- /dev/null +++ b/x/ibc/20-transfer/alias.go @@ -0,0 +1,61 @@ +package transfer + +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +const ( + DefaultPacketTimeout = keeper.DefaultPacketTimeout + DefaultCodespace = types.DefaultCodespace + CodeInvalidAddress = types.CodeInvalidAddress + CodeErrSendPacket = types.CodeErrSendPacket + CodeInvalidPacketData = types.CodeInvalidPacketData + CodeInvalidChannelOrder = types.CodeInvalidChannelOrder + CodeInvalidPort = types.CodeInvalidPort + CodeInvalidVersion = types.CodeInvalidVersion + AttributeKeyReceiver = types.AttributeKeyReceiver + SubModuleName = types.SubModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + BoundPortID = types.BoundPortID +) + +var ( + // functions aliases + NewKeeper = keeper.NewKeeper + RegisterCodec = types.RegisterCodec + ErrInvalidAddress = types.ErrInvalidAddress + ErrSendPacket = types.ErrSendPacket + ErrInvalidPacketData = types.ErrInvalidPacketData + ErrInvalidChannelOrder = types.ErrInvalidChannelOrder + ErrInvalidPort = types.ErrInvalidPort + ErrInvalidVersion = types.ErrInvalidVersion + GetEscrowAddress = types.GetEscrowAddress + GetDenomPrefix = types.GetDenomPrefix + GetModuleAccountName = types.GetModuleAccountName + NewMsgTransfer = types.NewMsgTransfer + + // variable aliases + ModuleCdc = types.ModuleCdc + AttributeValueCategory = types.AttributeValueCategory +) + +type ( + Keeper = keeper.Keeper + BankKeeper = types.BankKeeper + ChannelKeeper = types.ChannelKeeper + ClientKeeper = types.ClientKeeper + ConnectionKeeper = types.ConnectionKeeper + SupplyKeeper = types.SupplyKeeper + MsgTransfer = types.MsgTransfer + PacketData = types.PacketData + PacketDataAlias = types.PacketDataAlias +) diff --git a/x/ibc/20-transfer/client/cli/tx.go b/x/ibc/20-transfer/client/cli/tx.go new file mode 100644 index 000000000000..e9e12f28d4a8 --- /dev/null +++ b/x/ibc/20-transfer/client/cli/tx.go @@ -0,0 +1,70 @@ +package cli + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +// IBC transfer flags +var ( + FlagSource = "source" +) + +// GetTxCmd returns the transaction commands for IBC fungible token transfer +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + txCmd := &cobra.Command{ + Use: "transfer", + Short: "IBC fungible token transfer transaction subcommands", + } + txCmd.AddCommand( + GetTransferTxCmd(cdc), + ) + + return txCmd +} + +// GetTransferTxCmd returns the command to create a NewMsgTransfer transaction +func GetTransferTxCmd(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "transfer [src-port] [src-channel] [receiver] [amount]", + Short: "Transfer fungible token through IBC", + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + ctx := context.NewCLIContext().WithCodec(cdc).WithBroadcastMode(flags.BroadcastBlock) + + sender := ctx.GetFromAddress() + srcPort := args[0] + srcChannel := args[1] + receiver, err := sdk.AccAddressFromBech32(args[2]) + if err != nil { + return err + } + + // parse coin trying to be sent + coins, err := sdk.ParseCoins(args[3]) + if err != nil { + return err + } + + source := viper.GetBool(FlagSource) + + msg := types.NewMsgTransfer(srcPort, srcChannel, coins, sender, receiver, source) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(ctx, txBldr, []sdk.Msg{msg}) + }, + } + cmd.Flags().Bool(FlagSource, false, "Pass flag for sending token from the source chain") + return cmd +} diff --git a/x/ibc/20-transfer/genesis.go b/x/ibc/20-transfer/genesis.go new file mode 100644 index 000000000000..facde722a27d --- /dev/null +++ b/x/ibc/20-transfer/genesis.go @@ -0,0 +1,17 @@ +package transfer + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +// InitGenesis sets distribution information for genesis +func InitGenesis(ctx sdk.Context, keeper Keeper) { + // check if the module account exists + moduleAcc := keeper.GetTransferAccount(ctx) + if moduleAcc == nil { + panic(fmt.Sprintf("%s module account has not been set", types.GetModuleAccountName())) + } +} diff --git a/x/ibc/20-transfer/handler.go b/x/ibc/20-transfer/handler.go new file mode 100644 index 000000000000..52f47fab9257 --- /dev/null +++ b/x/ibc/20-transfer/handler.go @@ -0,0 +1,24 @@ +package transfer + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +// HandleMsgTransfer defines the sdk.Handler for MsgTransfer +func HandleMsgTransfer(ctx sdk.Context, k Keeper, msg MsgTransfer) (res sdk.Result) { + err := k.SendTransfer(ctx, msg.SourcePort, msg.SourceChannel, msg.Amount, msg.Sender, msg.Receiver, msg.Source) + if err != nil { + return sdk.ResultFromError(err) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver.String()), + )) + + return sdk.Result{Events: ctx.EventManager().Events()} +} diff --git a/x/ibc/20-transfer/keeper/callbacks.go b/x/ibc/20-transfer/keeper/callbacks.go new file mode 100644 index 000000000000..dad340f24ad2 --- /dev/null +++ b/x/ibc/20-transfer/keeper/callbacks.go @@ -0,0 +1,185 @@ +package keeper + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +// nolint: unused +func (k Keeper) onChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + counterparty channeltypes.Counterparty, + version string, +) error { + if order != channeltypes.UNORDERED { + return types.ErrInvalidChannelOrder(k.codespace, order.String()) + } + + if counterparty.PortID != types.BoundPortID { + return types.ErrInvalidPort(k.codespace, portID) + } + + if strings.TrimSpace(version) != "" { + return types.ErrInvalidVersion(k.codespace, fmt.Sprintf("invalid version: %s", version)) + } + + // TODO: + // channelEscrowAddresses[channelIdentifier] = newAddress() + return nil +} + +// nolint: unused +func (k Keeper) onChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + counterparty channeltypes.Counterparty, + version string, + counterpartyVersion string, +) error { + if order != channeltypes.UNORDERED { + return types.ErrInvalidChannelOrder(k.codespace, order.String()) + } + + if counterparty.PortID != types.BoundPortID { + return types.ErrInvalidPort(k.codespace, portID) + } + + if strings.TrimSpace(version) != "" { + return types.ErrInvalidVersion(k.codespace, fmt.Sprintf("invalid version: %s", version)) + } + + if strings.TrimSpace(counterpartyVersion) != "" { + return types.ErrInvalidVersion(k.codespace, fmt.Sprintf("invalid counterparty version: %s", version)) + } + + // TODO: + // channelEscrowAddresses[channelIdentifier] = newAddress() + + return nil +} + +// nolint: unused +func (k Keeper) onChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + version string, +) error { + if strings.TrimSpace(version) != "" { + return types.ErrInvalidVersion(k.codespace, fmt.Sprintf("invalid version: %s", version)) + } + + return nil +} + +// nolint: unused +func (k Keeper) onChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // no-op + return nil +} + +// nolint: unused +func (k Keeper) onChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // no-op + return nil +} + +// nolint: unused +func (k Keeper) onChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // no-op + return nil +} + +// onRecvPacket is called when an FTTransfer packet is received +// nolint: unused +func (k Keeper) onRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) error { + var data types.PacketData + + err := data.UnmarshalJSON(packet.Data()) + if err != nil { + return types.ErrInvalidPacketData(k.codespace) + } + + return k.ReceiveTransfer( + ctx, packet.SourcePort(), packet.SourceChannel(), + packet.DestPort(), packet.DestChannel(), data, + ) +} + +// nolint: unused +func (k Keeper) onAcknowledgePacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, +) error { + // no-op + return nil +} + +// nolint: unused +func (k Keeper) onTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) error { + var data types.PacketData + + err := data.UnmarshalJSON(packet.Data()) + if err != nil { + return types.ErrInvalidPacketData(k.codespace) + } + + // check the denom prefix + prefix := types.GetDenomPrefix(packet.SourcePort(), packet.SourcePort()) + coins := make(sdk.Coins, len(data.Amount)) + for i, coin := range data.Amount { + coin := coin + if !strings.HasPrefix(coin.Denom, prefix) { + return sdk.ErrInvalidCoins(fmt.Sprintf("%s doesn't contain the prefix '%s'", coin.Denom, prefix)) + } + coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount) + } + + if data.Source { + escrowAddress := types.GetEscrowAddress(packet.DestChannel()) + return k.bankKeeper.SendCoins(ctx, escrowAddress, data.Sender, coins) + } + + // mint from supply + err = k.supplyKeeper.MintCoins(ctx, types.GetModuleAccountName(), data.Amount) + if err != nil { + return err + } + + return k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.GetModuleAccountName(), data.Sender, data.Amount) +} + +// nolint: unused +func (k Keeper) onTimeoutPacketClose(_ sdk.Context, _ channeltypes.Packet) { + panic("can't happen, only unordered channels allowed") +} diff --git a/x/ibc/20-transfer/keeper/keeper.go b/x/ibc/20-transfer/keeper/keeper.go new file mode 100644 index 000000000000..87ce6b543af6 --- /dev/null +++ b/x/ibc/20-transfer/keeper/keeper.go @@ -0,0 +1,67 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// DefaultPacketTimeout is the default packet timeout relative to the current block height +const ( + DefaultPacketTimeout = 1000 // NOTE: in blocks +) + +// Keeper defines the IBC transfer keeper +type Keeper struct { + storeKey sdk.StoreKey + cdc *codec.Codec + codespace sdk.CodespaceType + prefix []byte // prefix bytes for accessing the store + + clientKeeper types.ClientKeeper + connectionKeeper types.ConnectionKeeper + channelKeeper types.ChannelKeeper + bankKeeper types.BankKeeper + supplyKeeper types.SupplyKeeper +} + +// NewKeeper creates a new IBC transfer Keeper instance +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType, + clientk types.ClientKeeper, connk types.ConnectionKeeper, + chank types.ChannelKeeper, bk types.BankKeeper, + sk types.SupplyKeeper, +) Keeper { + + // ensure ibc transfer module account is set + if addr := sk.GetModuleAddress(types.GetModuleAccountName()); addr == nil { + panic("the IBC transfer module account has not been set") + } + + return Keeper{ + storeKey: key, + cdc: cdc, + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/transfer", + prefix: []byte(types.SubModuleName + "/"), // "transfer/" + clientKeeper: clientk, + connectionKeeper: connk, + channelKeeper: chank, + bankKeeper: bk, + supplyKeeper: sk, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName)) +} + +// GetTransferAccount returns the ICS20 - transfers ModuleAccount +func (k Keeper) GetTransferAccount(ctx sdk.Context) supplyexported.ModuleAccountI { + return k.supplyKeeper.GetModuleAccount(ctx, types.GetModuleAccountName()) +} diff --git a/x/ibc/20-transfer/keeper/relay.go b/x/ibc/20-transfer/keeper/relay.go new file mode 100644 index 000000000000..b13f34f3ca8c --- /dev/null +++ b/x/ibc/20-transfer/keeper/relay.go @@ -0,0 +1,178 @@ +package keeper + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/types" +) + +// SendTransfer handles transfer sending logic +func (k Keeper) SendTransfer( + ctx sdk.Context, + sourcePort, + sourceChannel string, + amount sdk.Coins, + sender, + receiver sdk.AccAddress, + isSourceChain bool, +) error { + // get the port and channel of the counterparty + channel, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) + if !found { + return channeltypes.ErrChannelNotFound(k.codespace, sourceChannel) + } + + destinationPort := channel.Counterparty.PortID + destinationChannel := channel.Counterparty.ChannelID + + // get the next sequence + sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) + if !found { + return channeltypes.ErrSequenceNotFound(k.codespace, "send") + } + + coins := make(sdk.Coins, len(amount)) + prefix := types.GetDenomPrefix(destinationPort, destinationChannel) + switch { + case isSourceChain: + // build the receiving denomination prefix + for i, coin := range amount { + coin := coin + coins[i] = sdk.NewCoin(prefix+coin.Denom, coin.Amount) + } + default: + coins = amount + } + + return k.createOutgoingPacket(ctx, sequence, sourcePort, sourceChannel, destinationPort, destinationChannel, coins, sender, receiver, isSourceChain) +} + +// ReceiveTransfer handles transfer receiving logic +func (k Keeper) ReceiveTransfer( + ctx sdk.Context, + sourcePort, + sourceChannel, + destinationPort, + destinationChannel string, + data types.PacketData, +) error { + if data.Source { + prefix := types.GetDenomPrefix(destinationPort, destinationChannel) + for _, coin := range data.Amount { + coin := coin + if !strings.HasPrefix(coin.Denom, prefix) { + return sdk.ErrInvalidCoins(fmt.Sprintf("%s doesn't contain the prefix '%s'", coin.Denom, prefix)) + } + } + + // mint new tokens if the source of the transfer is the same chain + err := k.supplyKeeper.MintCoins(ctx, types.GetModuleAccountName(), data.Amount) + if err != nil { + return err + } + + // send to receiver + return k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.GetModuleAccountName(), data.Receiver, data.Amount) + } + + // unescrow tokens + + // check the denom prefix + prefix := types.GetDenomPrefix(sourcePort, sourceChannel) + coins := make(sdk.Coins, len(data.Amount)) + for i, coin := range data.Amount { + coin := coin + if !strings.HasPrefix(coin.Denom, prefix) { + return sdk.ErrInvalidCoins(fmt.Sprintf("%s doesn't contain the prefix '%s'", coin.Denom, prefix)) + } + coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount) + } + + escrowAddress := types.GetEscrowAddress(destinationChannel) + return k.bankKeeper.SendCoins(ctx, escrowAddress, data.Receiver, coins) + +} + +func (k Keeper) createOutgoingPacket( + ctx sdk.Context, + seq uint64, + sourcePort, + sourceChannel, + destinationPort, + destinationChannel string, + amount sdk.Coins, + sender sdk.AccAddress, + receiver sdk.AccAddress, + isSourceChain bool, +) error { + if isSourceChain { + // escrow tokens if the destination chain is the same as the sender's + escrowAddress := types.GetEscrowAddress(sourceChannel) + + prefix := types.GetDenomPrefix(destinationPort, destinationChannel) + coins := make(sdk.Coins, len(amount)) + for i, coin := range amount { + coin := coin + if !strings.HasPrefix(coin.Denom, prefix) { + return sdk.ErrInvalidCoins(fmt.Sprintf("%s doesn't contain the prefix '%s'", coin.Denom, prefix)) + } + coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount) + } + + err := k.bankKeeper.SendCoins(ctx, sender, escrowAddress, coins) + if err != nil { + return err + } + + } else { + // burn vouchers from the sender's balance if the source is from another chain + prefix := types.GetDenomPrefix(sourcePort, sourceChannel) + for _, coin := range amount { + coin := coin + if !strings.HasPrefix(coin.Denom, prefix) { + return sdk.ErrInvalidCoins(fmt.Sprintf("%s doesn't contain the prefix '%s'", coin.Denom, prefix)) + } + } + + // transfer the coins to the module account and burn them + err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.GetModuleAccountName(), amount) + if err != nil { + return err + } + + // burn from supply + err = k.supplyKeeper.BurnCoins(ctx, types.GetModuleAccountName(), amount) + if err != nil { + return err + } + } + + packetData := types.PacketData{ + Amount: amount, + Sender: sender, + Receiver: receiver, + Source: isSourceChain, + } + + packetDataBz, err := packetData.MarshalJSON() + if err != nil { + return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeInvalidPacketData, "invalid packet data") + } + + packet := channeltypes.NewPacket( + seq, + uint64(ctx.BlockHeight())+DefaultPacketTimeout, + sourcePort, + sourceChannel, + destinationPort, + destinationChannel, + packetDataBz, + ) + + // generate the capability key + key := sdk.NewKVStoreKey(types.BoundPortID) + return k.channelKeeper.SendPacket(ctx, packet, key) +} diff --git a/x/ibc/20-transfer/module.go b/x/ibc/20-transfer/module.go new file mode 100644 index 000000000000..269be2377f6e --- /dev/null +++ b/x/ibc/20-transfer/module.go @@ -0,0 +1,18 @@ +package transfer + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer/client/cli" +) + +// Name returns the IBC transfer ICS name +func Name() string { + return SubModuleName +} + +// GetTxCmd returns the root tx command for the IBC transfer. +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} diff --git a/x/ibc/20-transfer/types/codec.go b/x/ibc/20-transfer/types/codec.go new file mode 100644 index 000000000000..061c96e9d32a --- /dev/null +++ b/x/ibc/20-transfer/types/codec.go @@ -0,0 +1,20 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" +) + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgTransfer{}, "ibc/transfer/MsgTransfer", nil) + cdc.RegisterConcrete(PacketData{}, "ibc/transfer/PacketData", nil) +} + +var ModuleCdc = codec.New() + +func init() { + RegisterCodec(ModuleCdc) + channel.RegisterCodec(ModuleCdc) + commitment.RegisterCodec(ModuleCdc) +} diff --git a/x/ibc/20-transfer/types/errors.go b/x/ibc/20-transfer/types/errors.go new file mode 100644 index 000000000000..090804150095 --- /dev/null +++ b/x/ibc/20-transfer/types/errors.go @@ -0,0 +1,49 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// transfer error codes +const ( + DefaultCodespace sdk.CodespaceType = SubModuleName + + CodeInvalidAddress sdk.CodeType = 101 + CodeErrSendPacket sdk.CodeType = 102 + CodeInvalidPacketData sdk.CodeType = 103 + CodeInvalidChannelOrder sdk.CodeType = 104 + CodeInvalidPort sdk.CodeType = 105 + CodeInvalidVersion sdk.CodeType = 106 +) + +// ErrInvalidAddress implements sdk.Error +func ErrInvalidAddress(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidAddress, msg) +} + +// ErrSendPacket implements sdk.Error +func ErrSendPacket(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeErrSendPacket, "failed to send packet") +} + +// ErrInvalidPacketData implements sdk.Error +func ErrInvalidPacketData(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidPacketData, "invalid packet data") +} + +// ErrInvalidChannelOrder implements sdk.Error +func ErrInvalidChannelOrder(codespace sdk.CodespaceType, order string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidChannelOrder, fmt.Sprintf("invalid channel order: %s", order)) +} + +// ErrInvalidPort implements sdk.Error +func ErrInvalidPort(codespace sdk.CodespaceType, portID string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidPort, fmt.Sprintf("invalid port ID: %s", portID)) +} + +// ErrInvalidVersion implements sdk.Error +func ErrInvalidVersion(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidVersion, msg) +} diff --git a/x/ibc/20-transfer/types/events.go b/x/ibc/20-transfer/types/events.go new file mode 100644 index 000000000000..759548ae1827 --- /dev/null +++ b/x/ibc/20-transfer/types/events.go @@ -0,0 +1,17 @@ +package types + +import ( + "fmt" + + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +// IBC transfer events +const ( + AttributeKeyReceiver = "receiver" +) + +// IBC transfer events vars +var ( + AttributeValueCategory = fmt.Sprintf("%s_%s", ibctypes.ModuleName, SubModuleName) +) diff --git a/x/ibc/20-transfer/types/expected_keepers.go b/x/ibc/20-transfer/types/expected_keepers.go new file mode 100644 index 000000000000..2e855dfd8079 --- /dev/null +++ b/x/ibc/20-transfer/types/expected_keepers.go @@ -0,0 +1,42 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// BankKeeper defines the expected bank keeper +type BankKeeper interface { + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error +} + +// ChannelKeeper defines the expected IBC channel keeper +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channel.Channel, found bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + SendPacket(ctx sdk.Context, packet channelexported.PacketI, portCapability sdk.CapabilityKey) error +} + +// ClientKeeper defines the expected IBC client keeper +type ClientKeeper interface { + GetConsensusState(ctx sdk.Context, clientID string) (connection clientexported.ConsensusState, found bool) +} + +// ConnectionKeeper defines the expected IBC connection keeper +type ConnectionKeeper interface { + GetConnection(ctx sdk.Context, connectionID string) (connection connection.ConnectionEnd, found bool) +} + +// SupplyKeeper expected supply keeper +type SupplyKeeper interface { + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) sdk.Error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error +} diff --git a/x/ibc/20-transfer/types/keys.go b/x/ibc/20-transfer/types/keys.go new file mode 100644 index 000000000000..177982bd169c --- /dev/null +++ b/x/ibc/20-transfer/types/keys.go @@ -0,0 +1,42 @@ +package types + +import ( + "fmt" + + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +const ( + // SubModuleName defines the IBC transfer name + SubModuleName = "transfer" + + // StoreKey is the store key string for IBC transfer + StoreKey = SubModuleName + + // RouterKey is the message route for IBC transfer + RouterKey = SubModuleName + + // QuerierRoute is the querier route for IBC transfer + QuerierRoute = SubModuleName + + // BoundPortID defines the name of the capability key + BoundPortID = "bank" +) + +// GetEscrowAddress returns the escrow address for the specified channel +func GetEscrowAddress(chanID string) sdk.AccAddress { + return sdk.AccAddress(crypto.AddressHash([]byte(chanID))) +} + +// GetDenomPrefix returns the receiving denomination prefix +func GetDenomPrefix(portID, channelID string) string { + return fmt.Sprintf("%s/%s", portID, channelID) +} + +// GetModuleAccountName returns the IBC transfer module account name for supply +func GetModuleAccountName() string { + return fmt.Sprintf("%s/%s", ibctypes.ModuleName, SubModuleName) +} diff --git a/x/ibc/20-transfer/types/msgs.go b/x/ibc/20-transfer/types/msgs.go new file mode 100644 index 000000000000..e2d4a725b6d1 --- /dev/null +++ b/x/ibc/20-transfer/types/msgs.go @@ -0,0 +1,75 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" +) + +type MsgTransfer struct { + SourcePort string `json:"source_port" yaml:"source_port"` // the port on which the packet will be sent + SourceChannel string `json:"source_channel" yaml:"source_channel"` // the channel by which the packet will be sent + Amount sdk.Coins `json:"amount" yaml:"amount"` // the tokens to be transferred + Sender sdk.AccAddress `json:"sender" yaml:"sender"` // the sender address + Receiver sdk.AccAddress `json:"receiver" yaml:"receiver"` // the recipient address on the destination chain + Source bool `json:"source" yaml:"source"` // indicates if the sending chain is the source chain of the tokens to be transferred +} + +// NewMsgTransfer creates a new MsgTransfer instance +func NewMsgTransfer( + sourcePort, sourceChannel string, amount sdk.Coins, sender, receiver sdk.AccAddress, source bool, +) MsgTransfer { + return MsgTransfer{ + SourcePort: sourcePort, + SourceChannel: sourceChannel, + Amount: amount, + Sender: sender, + Receiver: receiver, + Source: source, + } +} + +// Route implements sdk.Msg +func (MsgTransfer) Route() string { + return ibctypes.RouterKey +} + +// Type implements sdk.Msg +func (MsgTransfer) Type() string { + return "transfer" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgTransfer) ValidateBasic() sdk.Error { + if err := host.DefaultIdentifierValidator(msg.SourcePort); err != nil { + return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid port ID: %s", err.Error())) + } + if err := host.DefaultIdentifierValidator(msg.SourceChannel); err != nil { + return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid channel ID: %s", err.Error())) + } + if !msg.Amount.IsValid() { + return sdk.ErrInvalidCoins("transfer amount is invalid") + } + if !msg.Amount.IsAllPositive() { + return sdk.ErrInsufficientCoins("transfer amount must be positive") + } + if msg.Sender.Empty() { + return sdk.ErrInvalidAddress("missing sender address") + } + if msg.Receiver.Empty() { + return sdk.ErrInvalidAddress("missing recipient address") + } + return nil +} + +// GetSignBytes implements sdk.Msg +func (msg MsgTransfer) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners implements sdk.Msg +func (msg MsgTransfer) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Sender} +} diff --git a/x/ibc/20-transfer/types/packet.go b/x/ibc/20-transfer/types/packet.go new file mode 100644 index 000000000000..a9df673cc19f --- /dev/null +++ b/x/ibc/20-transfer/types/packet.go @@ -0,0 +1,70 @@ +package types + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// PacketData defines a struct for the packet payload +type PacketData struct { + Amount sdk.Coins `json:"amount" yaml:"amount"` // the tokens to be transferred + Sender sdk.AccAddress `json:"sender" yaml:"sender"` // the sender address + Receiver sdk.AccAddress `json:"receiver" yaml:"receiver"` // the recipient address on the destination chain + Source bool `json:"source" yaml:"source"` // indicates if the sending chain is the source chain of the tokens to be transferred +} + +func (pd PacketData) MarshalAmino() ([]byte, error) { + return ModuleCdc.MarshalBinaryBare(pd) +} + +func (pd *PacketData) UnmarshalAmino(bz []byte) (err error) { + return ModuleCdc.UnmarshalBinaryBare(bz, pd) +} + +func (pd PacketData) Marshal() []byte { + return ModuleCdc.MustMarshalBinaryBare(pd) +} + +type PacketDataAlias PacketData + +// MarshalJSON implements the json.Marshaler interface. +func (pd PacketData) MarshalJSON() ([]byte, error) { + return json.Marshal((PacketDataAlias)(pd)) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (pd *PacketData) UnmarshalJSON(bz []byte) (err error) { + return json.Unmarshal(bz, (*PacketDataAlias)(pd)) +} + +func (pd PacketData) String() string { + return fmt.Sprintf(`PacketData: + Amount: %s + Sender: %s + Receiver: %s + Source: %v`, + pd.Amount.String(), + pd.Sender, + pd.Receiver, + pd.Source, + ) +} + +// ValidateBasic performs a basic check of the packet fields +func (pd PacketData) ValidateBasic() sdk.Error { + if !pd.Amount.IsValid() { + return sdk.ErrInvalidCoins("transfer amount is invalid") + } + if !pd.Amount.IsAllPositive() { + return sdk.ErrInsufficientCoins("transfer amount must be positive") + } + if pd.Sender.Empty() { + return sdk.ErrInvalidAddress("missing sender address") + } + if pd.Receiver.Empty() { + return sdk.ErrInvalidAddress("missing recipient address") + } + return nil +} diff --git a/x/ibc/ante.go b/x/ibc/ante.go index 720a76afd5b4..0e1f7f635d70 100644 --- a/x/ibc/ante.go +++ b/x/ibc/ante.go @@ -1,59 +1,51 @@ package ibc -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" - host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" -) - -// TODO: Should extract timeout msgs too -func ExtractMsgPackets(msgs []sdk.Msg) (res []MsgPacket, abort bool) { - res = make([]MsgPacket, 0, len(msgs)) - for _, msg := range msgs { - msgp, ok := msg.(MsgPacket) - if ok { - res = append(res, msgp) - } - } - - if len(res) >= 2 { - first := res[0] - for _, msg := range res[1:] { - if len(msg.ChannelID) != 0 && msg.ChannelID != first.ChannelID { - return res, true - } - msg.ChannelID = first.ChannelID - } - } - - return -} - -func VerifyMsgPackets(ctx sdk.Context, channel channel.Manager, msgs []MsgPacket) error { - for _, msg := range msgs { - err := channel.Receive(ctx, msg.Proofs, msg.Height, msg.ReceiverPort(), msg.ChannelID, msg.Packet) - if err != nil { - return err - } - } - - return nil -} - -func NewAnteDecorator(channel channel.Manager) sdk.AnteDecorator { - return func(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - msgs, abort := ExtractMsgPackets(tx.GetMsgs()) - if abort { - return ctx, host.ErrInvalidPacket - } - - err := VerifyMsgPackets(ctx, channel, msgs) - if err != nil { - return ctx, sdkerrors.Wrap(host.ErrInvalidPacket, err.Error()) - } - - return next(ctx, tx, simulate) - } -} +// // TODO: Should extract timeout msgs too +// func ExtractMsgPackets(msgs []sdk.Msg) (res []MsgPacket, abort bool) { +// res = make([]MsgPacket, 0, len(msgs)) +// for _, msg := range msgs { +// msgp, ok := msg.(MsgPacket) +// if ok { +// res = append(res, msgp) +// } +// } + +// if len(res) >= 2 { +// first := res[0] +// for _, msg := range res[1:] { +// if len(msg.ChannelID) != 0 && msg.ChannelID != first.ChannelID { +// return res, true +// } +// msg.ChannelID = first.ChannelID +// } +// } + +// return +// } + +// func VerifyMsgPackets(ctx sdk.Context, channel channel.Manager, msgs []MsgPacket) error { +// for _, msg := range msgs { +// err := channel.Receive(ctx, msg.Proofs, msg.Height, msg.ReceiverPort(), msg.ChannelID, msg.Packet) +// if err != nil { +// return err +// } +// } + +// return nil +// } + +// func NewAnteDecorator(channel channel.Manager) sdk.AnteDecorator { +// return func(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { +// msgs, abort := ExtractMsgPackets(tx.GetMsgs()) +// if abort { +// return ctx, host.ErrInvalidPacket +// } + +// err := VerifyMsgPackets(ctx, channel, msgs) +// if err != nil { +// return ctx, sdkerrors.Wrap(host.ErrInvalidPacket, err.Error()) +// } + +// return next(ctx, tx, simulate) +// } +// } diff --git a/x/ibc/client/cli/cli.go b/x/ibc/client/cli/cli.go index e6163dfc7d57..5e58be761800 100644 --- a/x/ibc/client/cli/cli.go +++ b/x/ibc/client/cli/cli.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/02-client" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" mockbank "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank" "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -24,6 +25,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcTxCmd.AddCommand( ibcclient.GetTxCmd(cdc, storeKey), connection.GetTxCmd(cdc, storeKey), + transfer.GetTxCmd(cdc), mockbank.GetTxCmd(cdc), ) return ibcTxCmd diff --git a/x/ibc/handler.go b/x/ibc/handler.go index d3f700a4d970..3e432e57e2c5 100644 --- a/x/ibc/handler.go +++ b/x/ibc/handler.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" ) // NewHandler defines the IBC handler @@ -37,24 +38,28 @@ func NewHandler(k Keeper) sdk.Handler { case connection.MsgConnectionOpenConfirm: return connection.HandleMsgConnectionOpenConfirm(ctx, k.ConnectionKeeper, msg) - // // IBC channel msgs - // case channel.MsgChannelOpenInit: - // return channel.HandleMsgChannelOpenInit(ctx, k.ChannelKeeper, msg) + // // IBC channel msgs + // case channel.MsgChannelOpenInit: + // return channel.HandleMsgChannelOpenInit(ctx, k.ChannelKeeper, msg) - // case channel.MsgChannelOpenTry: - // return channel.HandleMsgChannelOpenTry(ctx, k.ChannelKeeper, msg) + // case channel.MsgChannelOpenTry: + // return channel.HandleMsgChannelOpenTry(ctx, k.ChannelKeeper, msg) - // case channel.MsgChannelOpenAck: - // return channel.HandleMsgChannelOpenAck(ctx, k.ChannelKeeper, msg) + // case channel.MsgChannelOpenAck: + // return channel.HandleMsgChannelOpenAck(ctx, k.ChannelKeeper, msg) - // case channel.MsgChannelOpenConfirm: - // return channel.HandleMsgChannelOpenConfirm(ctx, k.ChannelKeeper, msg) + // case channel.MsgChannelOpenConfirm: + // return channel.HandleMsgChannelOpenConfirm(ctx, k.ChannelKeeper, msg) - // case channel.MsgChannelCloseInit: - // return channel.HandleMsgChannelCloseInit(ctx, k.ChannelKeeper, msg) + // case channel.MsgChannelCloseInit: + // return channel.HandleMsgChannelCloseInit(ctx, k.ChannelKeeper, msg) - // case channel.MsgChannelCloseConfirm: - // return channel.HandleMsgChannelCloseConfirm(ctx, k.ChannelKeeper, msg) + // case channel.MsgChannelCloseConfirm: + // return channel.HandleMsgChannelCloseConfirm(ctx, k.ChannelKeeper, msg) + + // IBC transfer msgs + case transfer.MsgTransfer: + return transfer.HandleMsgTransfer(ctx, k.TransferKeeper, msg) default: errMsg := fmt.Sprintf("unrecognized IBC message type: %T", msg) diff --git a/x/ibc/keeper/keeper.go b/x/ibc/keeper/keeper.go index 52158b48b140..2145416efb55 100644 --- a/x/ibc/keeper/keeper.go +++ b/x/ibc/keeper/keeper.go @@ -7,6 +7,7 @@ import ( connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" port "github.com/cosmos/cosmos-sdk/x/ibc/05-port" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" ) // Keeper defines each ICS keeper for IBC @@ -15,19 +16,25 @@ type Keeper struct { ConnectionKeeper connection.Keeper ChannelKeeper channel.Keeper PortKeeper port.Keeper + TransferKeeper transfer.Keeper } // NewKeeper creates a new ibc Keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Keeper { +func NewKeeper( + cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType, + bk transfer.BankKeeper, sk transfer.SupplyKeeper, +) Keeper { clientKeeper := client.NewKeeper(cdc, key, codespace) connectionKeeper := connection.NewKeeper(cdc, key, codespace, clientKeeper) portKeeper := port.NewKeeper(cdc, key, codespace) channelKeeper := channel.NewKeeper(cdc, key, codespace, clientKeeper, connectionKeeper, portKeeper) + transferKeeper := transfer.NewKeeper(cdc, key, codespace, clientKeeper, connectionKeeper, channelKeeper, bk, sk) return Keeper{ ClientKeeper: clientKeeper, ConnectionKeeper: connectionKeeper, ChannelKeeper: channelKeeper, PortKeeper: portKeeper, + TransferKeeper: transferKeeper, } } diff --git a/x/ibc/mock/bank/alias.go b/x/ibc/mock/bank/alias.go index 230489e7099c..7447068ba7e7 100644 --- a/x/ibc/mock/bank/alias.go +++ b/x/ibc/mock/bank/alias.go @@ -5,17 +5,14 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/internal/types" ) -// nolint type ( - MsgTransfer = types.MsgTransfer - MsgRecvTransferPacket = types.MsgRecvTransferPacket - Keeper = keeper.Keeper + MsgRecvPacket = types.MsgRecvPacket + Keeper = keeper.Keeper ) const ( ModuleName = types.ModuleName StoreKey = types.StoreKey - TStoreKey = types.TStoreKey QuerierRoute = types.QuerierRoute RouterKey = types.RouterKey ) @@ -24,7 +21,6 @@ const ( var ( RegisterCdc = types.RegisterCodec - NewKeeper = keeper.NewKeeper - NewMsgTransfer = types.NewMsgTransfer - NewMsgRecvTransferPacket = types.NewMsgRecvTransferPacket + NewKeeper = keeper.NewKeeper + NewMsgRecvPacket = types.NewMsgRecvPacket ) diff --git a/x/ibc/mock/bank/client/cli/flags.go b/x/ibc/mock/bank/client/cli/flags.go deleted file mode 100644 index 24218867aa86..000000000000 --- a/x/ibc/mock/bank/client/cli/flags.go +++ /dev/null @@ -1,27 +0,0 @@ -package cli - -import ( - flag "github.com/spf13/pflag" -) - -const ( - FlagSrcPort = "src-port" - FlagSrcChannel = "src-channel" - FlagDenom = "denom" - FlagAmount = "amount" - FlagReceiver = "receiver" - FlagSource = "source" -) - -var ( - FsTransfer = flag.NewFlagSet("", flag.ContinueOnError) -) - -func init() { - FsTransfer.String(FlagSrcPort, "", "the source port ID") - FsTransfer.String(FlagSrcChannel, "", "the source channel ID") - FsTransfer.String(FlagDenom, "", "the denomination of the token to be transferred") - FsTransfer.String(FlagAmount, "", "the amount of the token to be transferred") - FsTransfer.String(FlagReceiver, "", "the recipient") - FsTransfer.Bool(FlagSource, true, "indicate if the sending chain is the source chain of the token") -} diff --git a/x/ibc/mock/bank/client/cli/tx.go b/x/ibc/mock/bank/client/cli/tx.go index a7d96935a0ff..4bb1fb2fc7d3 100644 --- a/x/ibc/mock/bank/client/cli/tx.go +++ b/x/ibc/mock/bank/client/cli/tx.go @@ -13,68 +13,24 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/internal/types" "github.com/spf13/cobra" - "github.com/spf13/viper" ) func GetTxCmd(cdc *codec.Codec) *cobra.Command { txCmd := &cobra.Command{ Use: "ibcmockbank", Short: "IBC mockbank module transaction subcommands", - // RunE: client.ValidateCmd, } txCmd.AddCommand( - GetTransferTxCmd(cdc), GetMsgRecvPacketCmd(cdc), ) return txCmd } -func GetTransferTxCmd(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "transfer --src-port --src-channel --denom --amount --receiver --source ", - Short: "Transfer tokens across chains through IBC", - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - ctx := context.NewCLIContext().WithCodec(cdc).WithBroadcastMode(flags.BroadcastBlock) - - sender := ctx.GetFromAddress() - receiver := viper.GetString(FlagReceiver) - denom := viper.GetString(FlagDenom) - srcPort := viper.GetString(FlagSrcPort) - srcChan := viper.GetString(FlagSrcChannel) - source := viper.GetBool(FlagSource) - - amount, ok := sdk.NewIntFromString(viper.GetString(FlagAmount)) - if !ok { - return fmt.Errorf("invalid amount") - } - - msg := types.NewMsgTransfer(srcPort, srcChan, denom, amount, sender, receiver, source) - if err := msg.ValidateBasic(); err != nil { - return err - } - - return utils.GenerateOrBroadcastMsgs(ctx, txBldr, []sdk.Msg{msg}) - }, - } - - cmd.Flags().AddFlagSet(FsTransfer) - - cmd.MarkFlagRequired(FlagSrcPort) - cmd.MarkFlagRequired(FlagSrcChannel) - cmd.MarkFlagRequired(FlagDenom) - cmd.MarkFlagRequired(FlagAmount) - cmd.MarkFlagRequired(FlagReceiver) - - cmd = client.PostCommands(cmd)[0] - - return cmd -} - // GetMsgRecvPacketCmd returns the command to create a MsgRecvTransferPacket transaction func GetMsgRecvPacketCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ @@ -85,19 +41,20 @@ func GetMsgRecvPacketCmd(cdc *codec.Codec) *cobra.Command { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) cliCtx := context.NewCLIContext().WithCodec(cdc).WithBroadcastMode(flags.BroadcastBlock) - var packet types.Packet + var packet channel.Packet if err := cdc.UnmarshalJSON([]byte(args[0]), &packet); err != nil { fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...\n") contents, err := ioutil.ReadFile(args[0]) if err != nil { return fmt.Errorf("error opening packet file: %v", err) } - if err := packet.UnmarshalJSON(contents); err != nil { + + if err := cdc.UnmarshalJSON(contents, packet); err != nil { return fmt.Errorf("error unmarshalling packet file: %v", err) } } - var proof ics23.Proof + var proof commitment.Proof if err := cdc.UnmarshalJSON([]byte(args[1]), &proof); err != nil { fmt.Fprintf(os.Stderr, "failed to unmarshall input into struct, checking for file...\n") contents, err := ioutil.ReadFile(args[1]) @@ -114,7 +71,7 @@ func GetMsgRecvPacketCmd(cdc *codec.Codec) *cobra.Command { return fmt.Errorf("error height: %v", err) } - msg := types.NewMsgRecvTransferPacket(packet, []ics23.Proof{proof}, height, cliCtx.GetFromAddress()) + msg := types.NewMsgRecvPacket(packet, []commitment.Proof{proof}, height, cliCtx.GetFromAddress()) if err := msg.ValidateBasic(); err != nil { return err } diff --git a/x/ibc/mock/bank/handler.go b/x/ibc/mock/bank/handler.go index efe2bf9f5f8b..2ed32c4c837e 100644 --- a/x/ibc/mock/bank/handler.go +++ b/x/ibc/mock/bank/handler.go @@ -2,42 +2,24 @@ package mockbank import ( sdk "github.com/cosmos/cosmos-sdk/types" - ics04 "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { switch msg := msg.(type) { - case MsgTransfer: - return handleMsgTransfer(ctx, k, msg) - case MsgRecvTransferPacket: - return handleMsgRecvTransferPacket(ctx, k, msg) + case MsgRecvPacket: + return handleMsgRecvPacket(ctx, k, msg) default: return sdk.ErrUnknownRequest("failed to parse message").Result() } } } -func handleMsgTransfer(ctx sdk.Context, k Keeper, msg MsgTransfer) (res sdk.Result) { - err := k.SendTransfer(ctx, msg.SrcPort, msg.SrcChannel, msg.Denomination, msg.Amount, msg.Sender, msg.Receiver, msg.Source) +// handleMsgRecvPacket defines the sdk.Handler for MsgRecvPacket +func handleMsgRecvPacket(ctx sdk.Context, k Keeper, msg MsgRecvPacket) (res sdk.Result) { + err := k.ReceivePacket(ctx, msg.Packet, msg.Proofs[0], msg.Height) if err != nil { - return err.Result() - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, ics04.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), - )) - - return sdk.Result{Events: ctx.EventManager().Events()} -} - -func handleMsgRecvTransferPacket(ctx sdk.Context, k Keeper, msg MsgRecvTransferPacket) (res sdk.Result) { - err := k.ReceiveTransfer(ctx, msg.Packet, msg.Proofs[0], msg.Height) - if err != nil { - return err.Result() + return sdk.ResultFromError(err) } return sdk.Result{Events: ctx.EventManager().Events()} diff --git a/x/ibc/mock/bank/internal/keeper/keeper.go b/x/ibc/mock/bank/internal/keeper/keeper.go index ceb4ae71fbae..bdcabcf53e94 100644 --- a/x/ibc/mock/bank/internal/keeper/keeper.go +++ b/x/ibc/mock/bank/internal/keeper/keeper.go @@ -1,30 +1,22 @@ package keeper import ( - "fmt" - "strings" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - ics04 "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/internal/types" - "github.com/tendermint/tendermint/crypto" -) - -const ( - DefaultPacketTimeout = 1000 // default packet timeout relative to the current block height ) type Keeper struct { cdc *codec.Codec key sdk.StoreKey ck types.ChannelKeeper - bk types.BankKeeper + bk types.IbcBankKeeper } -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ck types.ChannelKeeper, bk types.BankKeeper) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ck types.ChannelKeeper, bk types.IbcBankKeeper) Keeper { return Keeper{ cdc: cdc, key: key, @@ -33,149 +25,20 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ck types.ChannelKeeper, bk ty } } -// SendTransfer handles transfer sending logic -func (k Keeper) SendTransfer(ctx sdk.Context, srcPort, srcChan string, denom string, amount sdk.Int, sender sdk.AccAddress, receiver string, source bool) sdk.Error { - // get the port and channel of the counterparty - channel, ok := k.ck.GetChannel(ctx, srcPort, srcChan) - if !ok { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), ics04.CodeChannelNotFound, "failed to get channel") - } - - dstPort := channel.Counterparty.PortID - dstChan := channel.Counterparty.ChannelID - - // get the next sequence - sequence, ok := k.ck.GetNextSequenceSend(ctx, srcPort, srcChan) - if !ok { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), ics04.CodeSequenceNotFound, "failed to retrieve sequence") - } - - if source { - // build the receiving denomination prefix - prefix := fmt.Sprintf("%s/%s", dstPort, dstChan) - denom = prefix + denom - } - - return k.createOutgoingPacket(ctx, sequence, srcPort, srcChan, dstPort, dstChan, denom, amount, sender, receiver, source) -} - -// ReceiveTransfer handles transfer receiving logic -func (k Keeper) ReceiveTransfer(ctx sdk.Context, packet exported.PacketI, proof ics23.Proof, height uint64) sdk.Error { - _, err := k.ck.RecvPacket(ctx, packet, proof, height, nil) +// ReceivePacket handles receiving packet +func (k Keeper) ReceivePacket(ctx sdk.Context, packet channelexported.PacketI, proof commitment.ProofI, height uint64) error { + _, err := k.ck.RecvPacket(ctx, packet, proof, height, nil, k.key) if err != nil { return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeErrReceivePacket, "failed to receive packet") } - var data types.TransferPacketData + // only process ICS20 token transfer packet data now, + // that should be done in routing module. + var data transfer.PacketData err = data.UnmarshalJSON(packet.Data()) if err != nil { return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeInvalidPacketData, "invalid packet data") } - receiverAddr, err := sdk.AccAddressFromBech32(data.Receiver) - if err != nil { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeInvalidReceiver, "invalid receiver address") - } - - if data.Source { - // mint tokens - - // check the denom prefix - prefix := fmt.Sprintf("%s/%s", packet.DestPort(), packet.DestChannel()) - if !strings.HasPrefix(data.Denomination, prefix) { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeIncorrectDenom, "incorrect denomination") - } - - _, err := k.bk.AddCoins(ctx, receiverAddr, sdk.Coins{sdk.NewCoin(data.Denomination, data.Amount)}) - if err != nil { - return err - } - - } else { - // unescrow tokens - - // check the denom prefix - prefix := fmt.Sprintf("%s/%s", packet.SourcePort(), packet.SourceChannel()) - if !strings.HasPrefix(data.Denomination, prefix) { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeIncorrectDenom, "incorrect denomination") - } - - escrowAddress := k.GetEscrowAddress(packet.DestChannel()) - err := k.bk.SendCoins(ctx, escrowAddress, receiverAddr, sdk.Coins{sdk.NewCoin(data.Denomination[len(prefix):], data.Amount)}) - if err != nil { - return err - } - } - - return nil -} - -func (k Keeper) createOutgoingPacket(ctx sdk.Context, seq uint64, srcPort, srcChan, dstPort, dstChan string, denom string, amount sdk.Int, sender sdk.AccAddress, receiver string, source bool) sdk.Error { - if source { - // escrow tokens - - // get escrow address - escrowAddress := k.GetEscrowAddress(srcChan) - - // check the denom prefix - prefix := fmt.Sprintf("%s/%s", dstPort, dstChan) - if !strings.HasPrefix(denom, prefix) { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeIncorrectDenom, "incorrect denomination") - } - - err := k.bk.SendCoins(ctx, sender, escrowAddress, sdk.Coins{sdk.NewCoin(denom[len(prefix):], amount)}) - if err != nil { - return err - } - - } else { - // burn vouchers from sender - - // check the denom prefix - prefix := fmt.Sprintf("%s/%s", srcPort, srcChan) - if !strings.HasPrefix(denom, prefix) { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeIncorrectDenom, "incorrect denomination") - } - - _, err := k.bk.SubtractCoins(ctx, sender, sdk.Coins{sdk.NewCoin(denom, amount)}) - if err != nil { - return err - } - } - - // build packet - packetData := types.TransferPacketData{ - Denomination: denom, - Amount: amount, - Sender: sender, - Receiver: receiver, - Source: source, - } - - packetDataBz, err := packetData.MarshalJSON() - if err != nil { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeInvalidPacketData, "invalid packet data") - } - - packet := types.NewPacket(seq, uint64(ctx.BlockHeight())+DefaultPacketTimeout, srcPort, srcChan, dstPort, dstChan, packetDataBz) - - err = k.ck.SendPacket(ctx, packet) - if err != nil { - return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeErrSendPacket, "failed to send packet") - } - - packetJson, _ := packet.MarshalJSON() - ctx.EventManager().EmitEvent(sdk.NewEvent( - ics04.EventTypeSendPacket, - sdk.NewAttribute(ics04.AttributeKeySenderPort, srcPort), - sdk.NewAttribute(ics04.AttributeKeyChannelID, srcChan), - sdk.NewAttribute(ics04.AttributeKeyPacket, string(packetJson)), - )) - - return nil -} - -// GetEscrowAddress returns the escrow address for the specified channel -func (k Keeper) GetEscrowAddress(chanID string) sdk.AccAddress { - return sdk.AccAddress(crypto.AddressHash([]byte(chanID))) + return k.bk.ReceiveTransfer(ctx, packet.SourcePort(), packet.SourceChannel(), packet.DestPort(), packet.DestChannel(), data) } diff --git a/x/ibc/mock/bank/internal/types/codec.go b/x/ibc/mock/bank/internal/types/codec.go index d20501ff5baa..ba05f5bb00f7 100644 --- a/x/ibc/mock/bank/internal/types/codec.go +++ b/x/ibc/mock/bank/internal/types/codec.go @@ -7,16 +7,13 @@ import ( ) func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(Packet{}, "ibcmockbank/Packet", nil) - cdc.RegisterConcrete(TransferPacketData{}, "ibcmockbank/TransferPacketData", nil) - cdc.RegisterConcrete(MsgTransfer{}, "ibcmockbank/MsgTransfer", nil) - cdc.RegisterConcrete(MsgRecvTransferPacket{}, "ibcmockbank/MsgRecvTransferPacket", nil) + cdc.RegisterConcrete(MsgRecvPacket{}, "ibcmockbank/MsgRecvPacket", nil) } -var MouduleCdc = codec.New() +var ModuleCdc = codec.New() func init() { - RegisterCodec(MouduleCdc) - channel.RegisterCodec(MouduleCdc) - commitment.RegisterCodec(MouduleCdc) + RegisterCodec(ModuleCdc) + channel.RegisterCodec(ModuleCdc) + commitment.RegisterCodec(ModuleCdc) } diff --git a/x/ibc/mock/bank/internal/types/errors.go b/x/ibc/mock/bank/internal/types/errors.go index 52582daccfe0..09cfc89784ff 100644 --- a/x/ibc/mock/bank/internal/types/errors.go +++ b/x/ibc/mock/bank/internal/types/errors.go @@ -6,12 +6,10 @@ import ( // ibcmockbank errors reserve 100 ~ 199. const ( - CodeIncorrectDenom sdk.CodeType = 101 - CodeInvalidAmount sdk.CodeType = 102 - CodeInvalidAddress sdk.CodeType = 103 - CodeInvalidReceiver sdk.CodeType = 104 - CodeErrSendPacket sdk.CodeType = 105 - CodeErrReceivePacket sdk.CodeType = 106 - CodeProofMissing sdk.CodeType = 107 - CodeInvalidPacketData sdk.CodeType = 108 + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeInvalidAddress sdk.CodeType = 101 + CodeErrReceivePacket sdk.CodeType = 102 + CodeProofMissing sdk.CodeType = 103 + CodeInvalidPacketData sdk.CodeType = 104 ) diff --git a/x/ibc/mock/bank/internal/types/expected_keepers.go b/x/ibc/mock/bank/internal/types/expected_keepers.go index 45156d2675b5..4c6e8e149169 100644 --- a/x/ibc/mock/bank/internal/types/expected_keepers.go +++ b/x/ibc/mock/bank/internal/types/expected_keepers.go @@ -3,30 +3,23 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) -type BankKeeper interface { - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) - - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error - - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error) +// IbcBankKeeper expected IBC transfer keeper +type IbcBankKeeper interface { + ReceiveTransfer(ctx sdk.Context, srcPort, srcChannel, destPort, destChannel string, data transfer.PacketData) error } +// ChannelKeeper expected IBC channel keeper type ChannelKeeper interface { - GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) - - GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) - RecvPacket( ctx sdk.Context, packet exported.PacketI, - proof ics23.Proof, + proof commitment.ProofI, proofHeight uint64, acknowledgement []byte, + portCapability sdk.CapabilityKey, ) (exported.PacketI, error) - - SendPacket(ctx sdk.Context, packet exported.PacketI) error } diff --git a/x/ibc/mock/bank/internal/types/keys.go b/x/ibc/mock/bank/internal/types/keys.go index a5e764dfbb72..2ffaa38eaa29 100644 --- a/x/ibc/mock/bank/internal/types/keys.go +++ b/x/ibc/mock/bank/internal/types/keys.go @@ -7,15 +7,9 @@ const ( // StoreKey is the string store representation StoreKey = ModuleName - // TStoreKey is the string transient store representation - TStoreKey = "transient_" + ModuleName - // QuerierRoute is the querier route for the module QuerierRoute = ModuleName // RouterKey is the msg router key for the module RouterKey = ModuleName - - // codespace - DefaultCodespace = ModuleName ) diff --git a/x/ibc/mock/bank/internal/types/msgs.go b/x/ibc/mock/bank/internal/types/msgs.go index ad87c838c1b4..61d414591e34 100644 --- a/x/ibc/mock/bank/internal/types/msgs.go +++ b/x/ibc/mock/bank/internal/types/msgs.go @@ -2,73 +2,20 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - ics04 "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" - ics23 "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" + commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ) -const ( - TypeMsgTransfer = "transfer" - TypeMsgRecvTransferPacket = "recv-transfer-packet" -) - -type MsgTransfer struct { - SrcPort string `json:"src_port" yaml:"src_port"` - SrcChannel string `json:"src_channel" yaml:"src_channel"` - Denomination string `json:"denomination" yaml:"denomination"` - Amount sdk.Int `json:"amount" yaml:"amount"` - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Receiver string `json:"receiver" yaml:"receiver"` - Source bool `json:"source" yaml:"source"` -} -type MsgRecvTransferPacket struct { - Packet ics04.PacketI `json:"packet" yaml:"packet"` - Proofs []ics23.Proof `json:"proofs" yaml:"proofs"` - Height uint64 `json:"height" yaml:"height"` - Signer sdk.AccAddress `json:"signer" yaml:"signer"` -} - -func NewMsgTransfer(srcPort, srcChannel string, denom string, amount sdk.Int, sender sdk.AccAddress, receiver string, source bool) MsgTransfer { - return MsgTransfer{ - SrcPort: srcPort, - SrcChannel: srcChannel, - Denomination: denom, - Amount: amount, - Sender: sender, - Receiver: receiver, - Source: source, - } -} - -func (MsgTransfer) Route() string { - return RouterKey -} - -func (MsgTransfer) Type() string { - return TypeMsgTransfer -} - -func (msg MsgTransfer) ValidateBasic() sdk.Error { - if !msg.Amount.IsPositive() { - return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAmount, "invalid amount") - } - - if msg.Sender.Empty() || len(msg.Receiver) == 0 { - return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAddress, "invalid address") - } - - return nil +type MsgRecvPacket struct { + Packet channel.PacketI `json:"packet" yaml:"packet"` + Proofs []commitment.Proof `json:"proofs" yaml:"proofs"` + Height uint64 `json:"height" yaml:"height"` + Signer sdk.AccAddress `json:"signer" yaml:"signer"` } -func (msg MsgTransfer) GetSignBytes() []byte { - return sdk.MustSortJSON(MouduleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgTransfer) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} - -func NewMsgRecvTransferPacket(packet ics04.PacketI, proofs []ics23.Proof, height uint64, signer sdk.AccAddress) MsgRecvTransferPacket { - return MsgRecvTransferPacket{ +// NewMsgRecvPacket creates a new MsgRecvPacket instance +func NewMsgRecvPacket(packet channel.PacketI, proofs []commitment.Proof, height uint64, signer sdk.AccAddress) MsgRecvPacket { + return MsgRecvPacket{ Packet: packet, Proofs: proofs, Height: height, @@ -76,15 +23,18 @@ func NewMsgRecvTransferPacket(packet ics04.PacketI, proofs []ics23.Proof, height } } -func (MsgRecvTransferPacket) Route() string { +// Route implements sdk.Msg +func (MsgRecvPacket) Route() string { return RouterKey } -func (MsgRecvTransferPacket) Type() string { - return TypeMsgRecvTransferPacket +// Type implements sdk.Msg +func (MsgRecvPacket) Type() string { + return "recv_packet" } -func (msg MsgRecvTransferPacket) ValidateBasic() sdk.Error { +// ValidateBasic implements sdk.Msg +func (msg MsgRecvPacket) ValidateBasic() sdk.Error { if msg.Proofs == nil { return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeProofMissing, "proof missing") } @@ -93,13 +43,19 @@ func (msg MsgRecvTransferPacket) ValidateBasic() sdk.Error { return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAddress, "invalid signer") } + if err := msg.Packet.ValidateBasic(); err != nil { + return err + } + return nil } -func (msg MsgRecvTransferPacket) GetSignBytes() []byte { - return sdk.MustSortJSON(MouduleCdc.MustMarshalJSON(msg)) +// GetSignBytes implements sdk.Msg +func (msg MsgRecvPacket) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } -func (msg MsgRecvTransferPacket) GetSigners() []sdk.AccAddress { +// GetSigners implements sdk.Msg +func (msg MsgRecvPacket) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } diff --git a/x/ibc/mock/bank/internal/types/packet.go b/x/ibc/mock/bank/internal/types/packet.go deleted file mode 100644 index c1b8fdbb9e6b..000000000000 --- a/x/ibc/mock/bank/internal/types/packet.go +++ /dev/null @@ -1,122 +0,0 @@ -package types - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" -) - -var _ exported.PacketI = Packet{} - -// Packet defines a type that carries data across different chains through IBC -type Packet struct { - sequence uint64 // number corresponds to the order of sends and receives, where a packet with an earlier sequence number must be sent and received before a packet with a later sequence number. - timeout uint64 // indicates a consensus height on the destination chain after which the packet will no longer be processed, and will instead count as having timed-out. - sourcePort string // identifies the port on the sending chain. - sourceChannel string // identifies the channel end on the sending chain. - destinationPort string // identifies the port on the receiving chain. - destinationChannel string // identifies the channel end on the receiving chain. - data []byte // opaque value which can be defined by the application logic of the associated modules. -} - -// newPacket creates a new Packet instance -func NewPacket( - sequence, timeout uint64, sourcePort, sourceChannel, - destinationPort, destinationChannel string, data []byte, -) Packet { - return Packet{ - sequence, - timeout, - sourcePort, - sourceChannel, - destinationPort, - destinationChannel, - data, - } -} - -// Sequence implements PacketI interface -func (p Packet) Sequence() uint64 { return p.sequence } - -// TimeoutHeight implements PacketI interface -func (p Packet) TimeoutHeight() uint64 { return p.timeout } - -// SourcePort implements PacketI interface -func (p Packet) SourcePort() string { return p.sourcePort } - -// SourceChannel implements PacketI interface -func (p Packet) SourceChannel() string { return p.sourceChannel } - -// DestPort implements PacketI interface -func (p Packet) DestPort() string { return p.destinationPort } - -// DestChannel implements PacketI interface -func (p Packet) DestChannel() string { return p.destinationChannel } - -// Data implements PacketI interface -func (p Packet) Data() []byte { return p.data } - -func (p Packet) MarshalJSON() ([]byte, error) { - return MouduleCdc.MarshalJSON(p) -} - -func (p *Packet) UnmarshalJSON(bz []byte) (err error) { - return MouduleCdc.UnmarshalJSON(bz, p) -} - -// TransferPacketData defines a struct for the packet payload -type TransferPacketData struct { - Denomination string `json:"denomination" yaml:"denomination"` - Amount sdk.Int `json:"amount" yaml:"amount"` - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Receiver string `json:"receiver" yaml:"receiver"` - Source bool `json:"source" yaml:"source"` -} - -func (tpd TransferPacketData) MarshalAmino() ([]byte, error) { - return MouduleCdc.MarshalBinaryBare(tpd) -} - -func (tpd *TransferPacketData) UnmarshalAmino(bz []byte) (err error) { - return MouduleCdc.UnmarshalBinaryBare(bz, tpd) -} - -func (tpd TransferPacketData) Marshal() []byte { - return MouduleCdc.MustMarshalBinaryBare(tpd) -} - -func (tpd TransferPacketData) MarshalJSON() ([]byte, error) { - return MouduleCdc.MarshalJSON(tpd) -} - -func (tpd *TransferPacketData) UnmarshalJSON(bz []byte) (err error) { - return MouduleCdc.UnmarshalJSON(bz, tpd) -} - -func (tpd TransferPacketData) String() string { - return fmt.Sprintf(`TransferPacketData: - Denomination %s - Amount: %s - Sender: %s - Receiver: %s - Source: %v`, - tpd.Denomination, - tpd.Amount.String(), - tpd.Sender.String(), - tpd.Receiver, - tpd.Source, - ) -} - -func (tpd TransferPacketData) Validate() error { - if !tpd.Amount.IsPositive() { - return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAmount, "invalid amount") - } - - if tpd.Sender.Empty() || len(tpd.Receiver) == 0 { - return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAddress, "invalid address") - } - - return nil -} diff --git a/x/ibc/module.go b/x/ibc/module.go index a09d8cb2bc3b..bdca95fc585d 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" client "github.com/cosmos/cosmos-sdk/x/ibc/02-client" channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/20-transfer" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/types" @@ -40,6 +41,7 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { client.RegisterCodec(cdc) channel.RegisterCodec(cdc) commitment.RegisterCodec(cdc) + transfer.RegisterCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the staking @@ -114,6 +116,8 @@ func (am AppModule) NewQuerierHandler() sdk.Querier { // InitGenesis performs genesis initialization for the staking module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + // check if the IBC transfer module account is set + transfer.InitGenesis(ctx, am.keeper.TransferKeeper) return []abci.ValidatorUpdate{} }