-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
ICS20 implementation #5204
Changes from 9 commits
d583964
e0f44bc
79ae5da
a745374
f06b2ec
87448ca
d353e16
46ad324
3531e3e
46c691c
a26369b
3052ed5
ef6f72e
8daa378
caede8e
22bc78f
67f5427
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
) |
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") | ||
} |
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 | ||
} |
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 { | ||
// 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()} | ||
} |
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of calling |
||
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))) | ||
} |
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) | ||
} |
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 | ||
) |
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 | ||
} |
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 | ||
) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)