diff --git a/x/ibc/04-channel/types/events.go b/x/ibc/04-channel/types/events.go index b82b3e8700cf..72d92cbf2237 100644 --- a/x/ibc/04-channel/types/events.go +++ b/x/ibc/04-channel/types/events.go @@ -12,6 +12,7 @@ const ( AttributeKeyReceiverPort = "receiver_port" AttributeKeyChannelID = "channel_id" AttributeKeySequence = "sequence" + AttributeKeyPacket = "packet" ) // IBC channel events vars diff --git a/x/ibc/client/cli/cli.go b/x/ibc/client/cli/cli.go index 4e25e3ce508f..e6163dfc7d57 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" + mockbank "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank" "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -23,6 +24,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { ibcTxCmd.AddCommand( ibcclient.GetTxCmd(cdc, storeKey), connection.GetTxCmd(cdc, storeKey), + mockbank.GetTxCmd(cdc), ) return ibcTxCmd } diff --git a/x/ibc/mock/bank/alias.go b/x/ibc/mock/bank/alias.go new file mode 100644 index 000000000000..230489e7099c --- /dev/null +++ b/x/ibc/mock/bank/alias.go @@ -0,0 +1,30 @@ +package mockbank + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/internal/types" +) + +// nolint +type ( + MsgTransfer = types.MsgTransfer + MsgRecvTransferPacket = types.MsgRecvTransferPacket + Keeper = keeper.Keeper +) + +const ( + ModuleName = types.ModuleName + StoreKey = types.StoreKey + TStoreKey = types.TStoreKey + QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey +) + +// nolint +var ( + RegisterCdc = types.RegisterCodec + + NewKeeper = keeper.NewKeeper + NewMsgTransfer = types.NewMsgTransfer + NewMsgRecvTransferPacket = types.NewMsgRecvTransferPacket +) diff --git a/x/ibc/mock/bank/client/cli/flags.go b/x/ibc/mock/bank/client/cli/flags.go new file mode 100644 index 000000000000..24218867aa86 --- /dev/null +++ b/x/ibc/mock/bank/client/cli/flags.go @@ -0,0 +1,27 @@ +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 new file mode 100644 index 000000000000..a7d96935a0ff --- /dev/null +++ b/x/ibc/mock/bank/client/cli/tx.go @@ -0,0 +1,128 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "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" + ics23 "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{ + Use: "recv-packet [/path/to/packet-data.json] [/path/to/proof.json] [height]", + Short: "Creates and sends a SendPacket message", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc).WithBroadcastMode(flags.BroadcastBlock) + + var packet types.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 { + return fmt.Errorf("error unmarshalling packet file: %v", err) + } + } + + var proof ics23.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]) + if err != nil { + return fmt.Errorf("error opening proofs file: %v", err) + } + if err := cdc.UnmarshalJSON(contents, &proof); err != nil { + return fmt.Errorf("error unmarshalling proofs file: %v", err) + } + } + + height, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return fmt.Errorf("error height: %v", err) + } + + msg := types.NewMsgRecvTransferPacket(packet, []ics23.Proof{proof}, height, cliCtx.GetFromAddress()) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + cmd = client.PostCommands(cmd)[0] + return cmd +} diff --git a/x/ibc/mock/bank/handler.go b/x/ibc/mock/bank/handler.go new file mode 100644 index 000000000000..efe2bf9f5f8b --- /dev/null +++ b/x/ibc/mock/bank/handler.go @@ -0,0 +1,44 @@ +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) + 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) + 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.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 new file mode 100644 index 000000000000..ceb4ae71fbae --- /dev/null +++ b/x/ibc/mock/bank/internal/keeper/keeper.go @@ -0,0 +1,181 @@ +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" + "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 +} + +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ck types.ChannelKeeper, bk types.BankKeeper) Keeper { + return Keeper{ + cdc: cdc, + key: key, + ck: ck, + bk: bk, + } +} + +// 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) + if err != nil { + return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeErrReceivePacket, "failed to receive packet") + } + + var data types.TransferPacketData + 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))) +} diff --git a/x/ibc/mock/bank/internal/types/codec.go b/x/ibc/mock/bank/internal/types/codec.go new file mode 100644 index 000000000000..e0762be3f3d2 --- /dev/null +++ b/x/ibc/mock/bank/internal/types/codec.go @@ -0,0 +1,24 @@ +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" + "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/merkle" +) + +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) +} + +var MouduleCdc = codec.New() + +func init() { + RegisterCodec(MouduleCdc) + channel.RegisterCodec(MouduleCdc) + commitment.RegisterCodec(MouduleCdc) + merkle.RegisterCodec(MouduleCdc) +} diff --git a/x/ibc/mock/bank/internal/types/errors.go b/x/ibc/mock/bank/internal/types/errors.go new file mode 100644 index 000000000000..52582daccfe0 --- /dev/null +++ b/x/ibc/mock/bank/internal/types/errors.go @@ -0,0 +1,17 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// 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 +) diff --git a/x/ibc/mock/bank/internal/types/expected_keepers.go b/x/ibc/mock/bank/internal/types/expected_keepers.go new file mode 100644 index 000000000000..45156d2675b5 --- /dev/null +++ b/x/ibc/mock/bank/internal/types/expected_keepers.go @@ -0,0 +1,32 @@ +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" +) + +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) +} + +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, + proofHeight uint64, + acknowledgement []byte, + ) (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 new file mode 100644 index 000000000000..a5e764dfbb72 --- /dev/null +++ b/x/ibc/mock/bank/internal/types/keys.go @@ -0,0 +1,21 @@ +package types + +const ( + // ModuleName is the name of the module + ModuleName = "ibcmockbank" + + // 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 new file mode 100644 index 000000000000..ad87c838c1b4 --- /dev/null +++ b/x/ibc/mock/bank/internal/types/msgs.go @@ -0,0 +1,105 @@ +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" +) + +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 +} + +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{ + Packet: packet, + Proofs: proofs, + Height: height, + Signer: signer, + } +} + +func (MsgRecvTransferPacket) Route() string { + return RouterKey +} + +func (MsgRecvTransferPacket) Type() string { + return TypeMsgRecvTransferPacket +} + +func (msg MsgRecvTransferPacket) ValidateBasic() sdk.Error { + if msg.Proofs == nil { + return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeProofMissing, "proof missing") + } + + if msg.Signer.Empty() { + return sdk.NewError(sdk.CodespaceType(DefaultCodespace), CodeInvalidAddress, "invalid signer") + } + + return nil +} + +func (msg MsgRecvTransferPacket) GetSignBytes() []byte { + return sdk.MustSortJSON(MouduleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgRecvTransferPacket) 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 new file mode 100644 index 000000000000..c1b8fdbb9e6b --- /dev/null +++ b/x/ibc/mock/bank/internal/types/packet.go @@ -0,0 +1,122 @@ +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/mock/bank/module.go b/x/ibc/mock/bank/module.go new file mode 100644 index 000000000000..e033d1de5666 --- /dev/null +++ b/x/ibc/mock/bank/module.go @@ -0,0 +1,110 @@ +package mockbank + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank/client/cli" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +func (AppModuleBasic) Name() string { + return ModuleName +} + +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCdc(cdc) +} + +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return nil +} + +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return nil +} + +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + //noop +} + +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return nil +} + +type AppModule struct { + AppModuleBasic + k Keeper +} + +func NewAppModule(k Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + k: k, + } +} + +func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + +} + +func (AppModule) Name() string { + return ModuleName +} + +func (AppModule) Route() string { + return ModuleName +} + +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.k) +} + +func (am AppModule) QuerierRoute() string { + return ModuleName +} + +func (am AppModule) NewQuerierHandler() sdk.Querier { + return nil +} + +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return nil +} + +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { + +} + +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// GetTxCmd returns the root tx command for the ibc-channel module. +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +}