Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICS20 implementation #5204

Merged
merged 17 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions x/ibc/mock/bank/alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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
Keeper = keeper.Keeper
)

// nolint
var (
ModuleName = types.ModuleName
StoreKey = types.StoreKey
TStoreKey = types.TStoreKey
QuerierRoute = types.QuerierRoute
RouterKey = types.RouterKey

RegisterCdc = types.RegisterCodec

NewMsgTransfer = types.NewMsgTransfer
)
31 changes: 31 additions & 0 deletions x/ibc/mock/bank/client/cli/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cli

import (
flag "github.com/spf13/pflag"
)

const (
FlagSrcPort = "src-port"
FlagSrcChannel = "src-channel"
FlagAmount = "amount"
FlagReceiver = "receiver"
FlagSource = "source"
FlagTimeout = "timeout"
FlagProofPath = "proof-path"
FlagProofHeight = "proof-height"
)

var (
FsTransfer = flag.NewFlagSet("", flag.ContinueOnError)
)

func init() {
FsTransfer.String(FlagSrcPort, "", "the source port ID")
FsTransfer.String(FlagSrcChannel, "", "the source channel ID")
FsTransfer.String(FlagAmount, "", "the amount to be transferred")
FsTransfer.String(FlagReceiver, "", "the recipient")
FsTransfer.Bool(FlagSource, true, "indicate if the sender is the source chain of the token")
FsTransfer.Uint64(FlagTimeout, 0, "the block height after which the packet will expire")
FsTransfer.String(FlagProofPath, "", "the path of the proof file")
FsTransfer.Uint64(FlagProofHeight, 0, "the block height at which the proof is generated")
}
89 changes: 89 additions & 0 deletions x/ibc/mock/bank/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cli

import (
"io/ioutil"

"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"
mockbank "github.com/cosmos/cosmos-sdk/x/ibc/mock/bank"
"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(
TransferTxCmd(cdc),
)

return txCmd
}

func TransferTxCmd(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "transfer --src-port <src port> --src-channel <src channel> --amount <amount> --receiver <receiver> --source <source> --timeout <timeout> " +
"[--proof-path <proof-path> --proof-height <proof-height>]",
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)
srcPort := viper.GetString(FlagSrcPort)
srcChan := viper.GetString(FlagSrcChannel)
source := viper.GetBool(FlagSource)
timeout := viper.GetUint64(FlagTimeout)

amountStr := viper.GetString(FlagAmount)
amount, err := sdk.ParseCoin(amountStr)
if err != nil {
return err
}

var proof ics23.Proof
var proofHeight uint64

proofPath := viper.GetString(FlagProofPath)
if proofPath != "" {
proofBz, err := ioutil.ReadFile(proofPath)
if err != nil {
return err
}

if err := cdc.UnmarshalJSON(proofBz, &proof); err != nil {
return err
}

proofHeight = viper.GetUint64(FlagProofHeight)
}

msg := mockbank.NewMsgTransfer(srcPort, srcChan, amount, sender, receiver, source, timeout, proof, proofHeight)
if err := msg.ValidateBasic(); err != nil {
return err
}

return utils.GenerateOrBroadcastMsgs(ctx, txBldr, []sdk.Msg{msg})
},
}

cmd.MarkFlagRequired(FlagSrcPort)
cmd.MarkFlagRequired(FlagSrcChannel)
cmd.MarkFlagRequired(FlagAmount)
cmd.MarkFlagRequired(FlagReceiver)
cmd.MarkFlagRequired(FlagTimeout)

cmd = client.PostCommands(cmd)[0]

return cmd
}
34 changes: 34 additions & 0 deletions x/ibc/mock/bank/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package mockbank

import (
sdk "github.com/cosmos/cosmos-sdk/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)
default:
return sdk.ErrUnknownRequest("failed to parse message").Result()
}
}
}

func handleMsgTransfer(ctx sdk.Context, k Keeper, msg MsgTransfer) (res sdk.Result) {
if msg.Proof != nil {
// send packet
err := k.SendTransfer(ctx, msg.SrcPort, msg.SrcChannel, msg.Amount, msg.Sender, msg.Receiver, msg.Source, msg.Timeout)
if err != nil {
return err.Result()
}
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled through the message router, cc @mossid

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or ICS 26 if we don't go with that)

// receive packet
err := k.ReceiveTransfer(ctx, msg.SrcPort, msg.SrcChannel, msg.Amount, msg.Sender, msg.Receiver, msg.Source, msg.Timeout, msg.Proof, msg.ProofHeight)
if err != nil {
return err.Result()
}
}

return sdk.Result{Events: ctx.EventManager().Events()}
}
134 changes: 134 additions & 0 deletions x/ibc/mock/bank/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package keeper

import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/ibc"
chantypes "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"
)

type Keeper struct {
cdc *codec.Codec
key sdk.StoreKey
ibck ibc.Keeper
bk types.BankKeeper
}

func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ibck ibc.Keeper, bk types.BankKeeper) Keeper {
return Keeper{
cdc: cdc,
key: key,
ibck: ibck,
bk: bk,
}
}

// SendTransfer handles transfer sending logic
func (k Keeper) SendTransfer(ctx sdk.Context, srcPort, srcChan string, amount sdk.Coin, sender sdk.AccAddress, receiver string, source bool, timeout uint64) sdk.Error {
// get the port and channel of the counterparty
channel, ok := k.ibck.ChannelKeeper.GetChannel(ctx, srcPort, srcChan)
if !ok {
return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), chantypes.CodeChannelNotFound, "failed to get channel")
}

dstPort := channel.Counterparty.PortID
dstChan := channel.Counterparty.ChannelID

// get the next sequence
sequence, ok := k.ibck.ChannelKeeper.GetNextSequenceSend(ctx, srcPort, srcChan)
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), chantypes.CodeSequenceNotFound, "failed to retrieve sequence")
}

if source {
// escrow tokens
escrowAddress := k.GetEscrowAddress(srcChan)
err := k.bk.SendCoins(ctx, sender, escrowAddress, sdk.Coins{amount})
if err != nil {
return err
}

} else {
// burn vouchers from sender
err := k.bk.BurnCoins(ctx, sender, sdk.Coins{amount})
if err != nil {
return err
}
}

// build packet
packetData := types.TransferPacketData{
Amount: amount,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The denomination needs to be altered, see ICS 20

Sender: sender,
Receiver: receiver,
Source: source,
}

packet := chantypes.NewPacket(sequence, timeout, srcPort, srcChan, dstPort, dstChan, packetData.Marshal())

err := k.ibck.ChannelKeeper.SendPacket(ctx, packet)
if err != nil {
return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeErrSendPacket, "failed to send packet")
}

return nil
}

// ReceiveTransfer handles transfer receiving logic
func (k Keeper) ReceiveTransfer(ctx sdk.Context, srcPort, srcChan string, amount sdk.Coin, sender sdk.AccAddress, receiver string, source bool, timeout uint64, proof ics23.Proof, proofHeight uint64) sdk.Error {
// get the port and channel of the counterparty
channel, ok := k.ibck.ChannelKeeper.GetChannel(ctx, srcPort, srcChan)
if !ok {
return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), chantypes.CodeChannelNotFound, "failed to get channel")
}

dstPort := channel.Counterparty.PortID
dstChan := channel.Counterparty.ChannelID

receiverAddr, err := sdk.AccAddressFromBech32(receiver)
if err != nil {
sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeInvalidReceiver, "invalid receiver address")
}

if source {
// mint tokens
_, err := k.bk.AddCoins(ctx, receiverAddr, sdk.Coins{amount})
if err != nil {
return err
}

} else {
// unescrow tokens
escrowAddress := k.GetEscrowAddress(dstChan)
err := k.bk.SendCoins(ctx, escrowAddress, receiverAddr, sdk.Coins{amount})
if err != nil {
return err
}
}

// build packet
packetData := types.TransferPacketData{
Amount: amount,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The denomination needs to be altered, see ICS 20

Sender: sender,
Receiver: receiver,
Source: source,
}

sequence := uint64(0) // unordered channel
packet := chantypes.NewPacket(sequence, timeout, srcPort, srcChan, dstPort, dstChan, packetData.Marshal())

_, err = k.ibck.ChannelKeeper.RecvPacket(ctx, packet, proof, proofHeight, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling recvPacket, the packet should be validated by the IBC module first, cc @mossid

if err != nil {
return sdk.NewError(sdk.CodespaceType(types.DefaultCodespace), types.CodeErrReceivePacket, "failed to receive packet")
}

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)))
}
16 changes: 16 additions & 0 deletions x/ibc/mock/bank/internal/types/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package types

import (
"github.com/cosmos/cosmos-sdk/codec"
)

func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(TransferPacketData{}, "ibcmockbank/TransferPacketData", nil)
cdc.RegisterConcrete(MsgTransfer{}, "ibcmockbank/MsgTransfer", nil)
}

var MouduleCdc = codec.New()

func init() {
RegisterCodec(MouduleCdc)
}
14 changes: 14 additions & 0 deletions x/ibc/mock/bank/internal/types/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// ibcmockbank errors reserve 100 ~ 199.
const (
CodeInvalidAmount sdk.CodeType = 101
CodeInvalidAddress sdk.CodeType = 102
CodeInvalidReceiver sdk.CodeType = 103
CodeErrSendPacket sdk.CodeType = 104
CodeErrReceivePacket sdk.CodeType = 105
)
19 changes: 19 additions & 0 deletions x/ibc/mock/bank/internal/types/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

type BankKeeper interface {
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Error)

SetTotalSupply(ctx sdk.Context, totalSupply sdk.Coin)

GetTotalSupply(ctx sdk.Context, denom string) (coin sdk.Coin, found bool)

IncreaseTotalSupply(ctx sdk.Context, amt sdk.Coin) sdk.Error

BurnCoins(ctx sdk.Context, fromAddr sdk.AccAddress, amt sdk.Coins) sdk.Error

SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error
}
21 changes: 21 additions & 0 deletions x/ibc/mock/bank/internal/types/keys.go
Original file line number Diff line number Diff line change
@@ -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
)
Loading