diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 298cf0027e47..6ea3d754f582 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -273,6 +273,7 @@ func TestCoinSend(t *testing.T) { require.Equal(t, http.StatusOK, res.StatusCode, body) } +/* func TestIBCTransfer(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) @@ -301,7 +302,7 @@ func TestIBCTransfer(t *testing.T) { // TODO: query ibc egress packet state } - +*/ func TestTxs(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) diff --git a/client/lcd/root.go b/client/lcd/root.go index bfa62f1cf0aa..a5f5f4580635 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -13,7 +13,6 @@ import ( auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/gorilla/mux" @@ -93,6 +92,5 @@ func createHandler(cdc *wire.Codec) http.Handler { stake.RegisterRoutes(cliCtx, r, cdc, kb) slashing.RegisterRoutes(cliCtx, r, cdc, kb) gov.RegisterRoutes(cliCtx, r, cdc) - return r } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 4ce6b2806de7..1ac0098af11f 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -53,7 +53,7 @@ type GaiaApp struct { accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper - ibcMapper ibc.Mapper + ibcKeeper ibc.Keeper stakeKeeper stake.Keeper slashingKeeper slashing.Keeper govKeeper gov.Keeper @@ -90,8 +90,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // add handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) + app.ibcKeeper = ibc.NewKeeper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) @@ -100,7 +100,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). + AddRoute("ibc", ibc.NewHandler(app.ibcKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)). AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). AddRoute("gov", gov.NewHandler(app.govKeeper)) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index df0fd3c1138e..75ba6eee0fb5 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -58,7 +58,6 @@ func main() { } ibcCmd.AddCommand( client.PostCommands( - ibccmd.IBCTransferCmd(cdc), ibccmd.IBCRelayCmd(cdc), )...) @@ -131,6 +130,7 @@ func main() { rootCmd.AddCommand( client.PostCommands( bankcmd.SendTxCmd(cdc), + bankcmd.IBCSendTxCmd(cdc), )...) // add proxy, version and key info diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index cab9d0ab0001..6455956fe77b 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -141,7 +141,7 @@ type GaiaApp struct { accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper - ibcMapper ibc.Mapper + ibcKeeper ibc.Keeper stakeKeeper stake.Keeper slashingKeeper slashing.Keeper paramsKeeper params.Keeper @@ -174,15 +174,15 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // add handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) + app.ibcKeeper = ibc.NewKeeper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). + AddRoute("ibc", ibc.NewHandler(app.ibcKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)) // initialize BaseApp diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 17f6de87b31e..e0403e8755c6 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -38,7 +38,7 @@ type BasecoinApp struct { accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper - ibcMapper ibc.Mapper + ibcKeeper ibc.Keeper } // NewBasecoinApp returns a reference to a new BasecoinApp given a logger and @@ -68,12 +68,12 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba }, ) app.coinKeeper = bank.NewKeeper(app.accountMapper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) + app.ibcKeeper = ibc.NewKeeper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)) + AddRoute("ibc", ibc.NewHandler(app.ibcKeeper)) // perform initialization logic app.SetInitChainer(app.initChainer) @@ -96,11 +96,9 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba // MakeCodec creates a new wire codec and registers all the necessary types // with the codec. func MakeCodec() *wire.Codec { - cdc := wire.NewCodec() - - wire.RegisterCrypto(cdc) - sdk.RegisterWire(cdc) - bank.RegisterWire(cdc) + var cdc = wire.NewCodec() + wire.RegisterCrypto(cdc) // Register crypto. + sdk.RegisterWire(cdc) // Register Msgs ibc.RegisterWire(cdc) auth.RegisterWire(cdc) diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 23183c87a4d3..c89851d4ac4a 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -65,7 +65,7 @@ func main() { rootCmd.AddCommand( client.PostCommands( bankcmd.SendTxCmd(cdc), - ibccmd.IBCTransferCmd(cdc), + bankcmd.IBCSendTxCmd(cdc), ibccmd.IBCRelayCmd(cdc), stakecmd.GetCmdCreateValidator(cdc), stakecmd.GetCmdEditValidator(cdc), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index d4cbac623923..9d2e57577226 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -44,7 +44,7 @@ type DemocoinApp struct { coinKeeper bank.Keeper coolKeeper cool.Keeper powKeeper pow.Keeper - ibcMapper ibc.Mapper + ibcKeeper ibc.Keeper stakeKeeper simplestake.Keeper // Manage getting and setting accounts @@ -78,14 +78,14 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { app.coinKeeper = bank.NewKeeper(app.accountMapper) app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.coinKeeper, app.RegisterCodespace(cool.DefaultCodespace)) app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.coinKeeper, app.RegisterCodespace(pow.DefaultCodespace)) - app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) + app.ibcKeeper = ibc.NewKeeper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace)) app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). AddRoute("cool", cool.NewHandler(app.coolKeeper)). AddRoute("pow", app.powKeeper.Handler). AddRoute("sketchy", sketchy.NewHandler()). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). + AddRoute("ibc", ibc.NewHandler(app.ibcKeeper)). AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)) // Initialize BaseApp. @@ -109,8 +109,8 @@ func MakeCodec() *wire.Codec { sdk.RegisterWire(cdc) // Register Msgs cool.RegisterWire(cdc) pow.RegisterWire(cdc) - bank.RegisterWire(cdc) ibc.RegisterWire(cdc) + bank.RegisterWire(cdc) simplestake.RegisterWire(cdc) // Register AppAccount diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 43f86504eb27..d8f0de044958 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -59,10 +59,7 @@ func main() { rootCmd.AddCommand( client.PostCommands( bankcmd.SendTxCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - ibccmd.IBCTransferCmd(cdc), + bankcmd.IBCSendTxCmd(cdc), )...) rootCmd.AddCommand( client.PostCommands( diff --git a/types/lib/value.go b/types/lib/value.go new file mode 100644 index 000000000000..f1d76dcea069 --- /dev/null +++ b/types/lib/value.go @@ -0,0 +1,43 @@ +package lib + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" +) + +type Value struct { + store sdk.KVStore + cdc *wire.Codec + key []byte +} + +func NewValue(store sdk.KVStore, cdc *wire.Codec, key []byte) Value { + return Value{ + store: store, + cdc: cdc, + key: key, + } +} + +func (v Value) MustGet(ptr interface{}) { + bz := v.store.Get(v.key) + v.cdc.MustUnmarshalBinary(bz, ptr) +} + +func (v Value) Get(ptr interface{}) bool { + bz := v.store.Get(v.key) + if bz == nil { + return false + } + v.cdc.MustUnmarshalBinary(bz, ptr) + return true +} + +func (v Value) Has() bool { + bz := v.store.Get(v.key) + return bz != nil +} + +func (v Value) Set(val interface{}) { + v.store.Set(v.key, v.cdc.MustMarshalBinary(val)) +} diff --git a/x/bank/client/cli/flags.go b/x/bank/client/cli/flags.go new file mode 100644 index 000000000000..53e83642564a --- /dev/null +++ b/x/bank/client/cli/flags.go @@ -0,0 +1,7 @@ +package cli + +const ( + FlagTo = "to" + FlagAmount = "amount" + FlagDestChain = "dest-chain-id" +) diff --git a/x/ibc/client/cli/ibctx.go b/x/bank/client/cli/ibctx.go similarity index 65% rename from x/ibc/client/cli/ibctx.go rename to x/bank/client/cli/ibctx.go index f44c736d891c..8f538ad06c63 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/bank/client/cli/ibctx.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "os" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" @@ -15,16 +14,12 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" -) -const ( - flagTo = "to" - flagAmount = "amount" - flagChain = "chain" + "github.com/cosmos/cosmos-sdk/x/bank" ) -// IBCTransferCmd implements the IBC transfer command. -func IBCTransferCmd(cdc *wire.Codec) *cobra.Command { +// IBC transfer command +func IBCSendTxCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "transfer", RunE: func(cmd *cobra.Command, args []string) error { @@ -48,32 +43,35 @@ func IBCTransferCmd(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().String(flagTo, "", "Address to send coins") - cmd.Flags().String(flagAmount, "", "Amount of coins to send") - cmd.Flags().String(flagChain, "", "Destination chain to send coins") - + cmd.Flags().String(FlagTo, "", "Address to send coins") + cmd.Flags().String(FlagAmount, "", "Amount of coins to send") + cmd.Flags().String(FlagDestChain, "", "Destination chain to send coins") return cmd } func buildMsg(from sdk.AccAddress) (sdk.Msg, error) { - amount := viper.GetString(flagAmount) + amount := viper.GetString(FlagAmount) coins, err := sdk.ParseCoins(amount) if err != nil { return nil, err } - dest := viper.GetString(flagTo) + dest := viper.GetString(FlagTo) bz, err := hex.DecodeString(dest) if err != nil { return nil, err } to := sdk.AccAddress(bz) - packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(client.FlagChainID), - viper.GetString(flagChain)) + payload := bank.PayloadSend{ + SrcAddr: from, + DestAddr: to, + Coins: coins, + } - msg := ibc.IBCTransferMsg{ - IBCPacket: packet, + msg := bank.MsgIBCSend{ + PayloadSend: payload, + DestChain: viper.GetString(FlagDestChain), } return msg, nil diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 92ac37c1e973..057161eedda8 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -16,12 +16,7 @@ import ( "github.com/spf13/viper" ) -const ( - flagTo = "to" - flagAmount = "amount" -) - -// SendTxCmd will create a send tx and sign it with the given key. +// SendTxCmd will create a send tx and sign it with the given key func SendTxCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "send", @@ -38,14 +33,13 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command { } toStr := viper.GetString(flagTo) - to, err := sdk.AccAddressFromBech32(toStr) if err != nil { return err } // parse coins trying to be sent - amount := viper.GetString(flagAmount) + amount := viper.GetString(FlagAmount) coins, err := sdk.ParseCoins(amount) if err != nil { return err @@ -73,8 +67,7 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().String(flagTo, "", "Address to send coins") - cmd.Flags().String(flagAmount, "", "Amount of coins to send") - + cmd.Flags().String(FlagTo, "", "Address to send coins") + cmd.Flags().String(FlagAmount, "", "Amount of coins to send") return cmd } diff --git a/x/bank/client/rest/rest.go b/x/bank/client/rest/rest.go new file mode 100644 index 000000000000..57b168e48e6b --- /dev/null +++ b/x/bank/client/rest/rest.go @@ -0,0 +1,16 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/wire" +) + +// RegisterRoutes - Central function to define routes that get registered by the main application +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { + + r.HandleFunc("/accounts/{address}/send", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") +} diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index c7baa96910d8..520ad422679e 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -16,11 +16,6 @@ import ( "github.com/gorilla/mux" ) -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { - r.HandleFunc("/accounts/{address}/send", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") -} - type sendBody struct { // fees is not used currently // Fees sdk.Coin `json="fees"` diff --git a/x/bank/msgs.go b/x/bank/msgs.go index 316d66ba1304..2293b2dfc586 100644 --- a/x/bank/msgs.go +++ b/x/bank/msgs.go @@ -222,3 +222,56 @@ func NewOutput(addr sdk.AccAddress, coins sdk.Coins) Output { } return output } + +//------------------------------------ +// MsgIBCSend - IBC transaction of the coin module + +// Implements sdk.Msg +type MsgIBCSend struct { + PayloadSend `json:"payload-send"` + DestChain string `json:"dest-chain"` +} + +func (msg MsgIBCSend) GetSignBytes() []byte { + bin, err := msgCdc.MarshalJSON(msg) + if err != nil { + panic(err) + } + return bin +} + +func (msg MsgIBCSend) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.PayloadSend.SrcAddr} +} + +//------------------------------------ +// PayloadSend - payload for IBC sending + +// Implements ibc.Payload +type PayloadSend struct { + SrcAddr sdk.AccAddress `json:"src-addr"` + DestAddr sdk.AccAddress `json:"dest-addr"` + Coins sdk.Coins `json:"coins"` +} + +func (p PayloadSend) Type() string { + return "bank" +} + +func (p PayloadSend) ValidateBasic() sdk.Error { + if !p.Coins.IsValid() { + return sdk.ErrInvalidCoins(p.Coins.String()) + } + if !p.Coins.IsPositive() { + return sdk.ErrInvalidCoins(p.Coins.String()) + } + return nil +} + +//-=------------------------------------- +// ReceiptSendFail + +// Implements sdk.Receipt +type ReceiptSendFail struct { + PayloadSend +} diff --git a/x/bank/wire.go b/x/bank/wire.go index f468d3e5307a..923653783026 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -8,6 +8,8 @@ import ( func RegisterWire(cdc *wire.Codec) { cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil) cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil) + cdc.RegisterConcrete(MsgIBCSend{}, "cosmos-sdk/IBCSend", nil) + } var msgCdc = wire.NewCodec() diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/bank/client/rest/ibcsendtx.go similarity index 84% rename from x/ibc/client/rest/transfer.go rename to x/ibc/bank/client/rest/ibcsendtx.go index 765208b05655..c4114a89744c 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/bank/client/rest/ibcsendtx.go @@ -12,14 +12,9 @@ import ( authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/ibc" - "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/x/ibc/bank" ) -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { - r.HandleFunc("/ibc/{destchain}/{address}/send", TransferRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") -} - type transferBody struct { // Fees sdk.Coin `json="fees"` Amount sdk.Coins `json:"amount"` @@ -64,9 +59,24 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C return } + from, err := sdk.AccAddressFromBech32(string(info.GetPubKey().Address())) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + // build message - packet := ibc.NewIBCPacket(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount, m.SrcChainID, destChainID) - msg := ibc.IBCTransferMsg{packet} + p := bank.PayloadCoins{ + SrcAddr: from, + DestAddr: to, + Coins: m.Amount, + } + + msg := ibc.MsgSend{ + Payload: p, + DestChain: destChainID, + } txCtx := authctx.TxContext{ Codec: cdc, diff --git a/x/ibc/bank/client/rest/rest.go b/x/ibc/bank/client/rest/rest.go new file mode 100644 index 000000000000..667b7ca2aef8 --- /dev/null +++ b/x/ibc/bank/client/rest/rest.go @@ -0,0 +1,14 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/wire" +) + +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { + r.HandleFunc("/ibc/{destchain}/{address}/send", TransferRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") +} diff --git a/x/ibc/bank/handler.go b/x/ibc/bank/handler.go new file mode 100644 index 000000000000..d706e74bc769 --- /dev/null +++ b/x/ibc/bank/handler.go @@ -0,0 +1,58 @@ +package bank + +import ( + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc" +) + +func unknownRequest(prefix string, ty interface{}) sdk.Result { + errMsg := prefix + reflect.TypeOf(ty).Name() + return sdk.ErrUnknownRequest(errMsg).Result() +} + +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case ibc.MsgSend: + return k.ch.Send(func(p ibc.Payload) sdk.Result { + switch p := msg.Payload.(type) { + case PayloadCoins: + return handlePayloadCoinsSend(ctx, k, p) + default: + return unknownRequest("Unrecognized ibc/bank payload type: ", p) + } + }, ctx, msg) + case ibc.MsgReceive: + return k.ch.Receive(func(ctx sdk.Context, p ibc.Payload) (ibc.Payload, sdk.Result) { + switch p := msg.Payload.(type) { + case PayloadCoins: + return handlePayloadCoinsReceive(ctx, k, p) + default: + return nil, unknownRequest("Unrecognized ibc/bank payload type: ", p) + } + + }, ctx, msg) + // case ibc.MsgRelay + default: + return unknownRequest("Unrecognized ibc/bank Msg type: ", msg) + } + } +} + +func handlePayloadCoinsSend(ctx sdk.Context, k Keeper, p PayloadCoins) sdk.Result { + _, tags, err := k.bk.SubtractCoins(ctx, p.SrcAddr, p.Coins) + if err != nil { + return err.Result() + } + return sdk.Result{Tags: tags} +} + +func handlePayloadCoinsReceive(ctx sdk.Context, k Keeper, p PayloadCoins) (ibc.Payload, sdk.Result) { + _, tags, err := k.bk.AddCoins(ctx, p.DestAddr, p.Coins) + if err != nil { + return PayloadCoinsFail{p}, err.Result() + } + return nil, sdk.Result{Tags: tags} +} diff --git a/x/ibc/bank/keeper.go b/x/ibc/bank/keeper.go new file mode 100644 index 000000000000..3f2ced28723b --- /dev/null +++ b/x/ibc/bank/keeper.go @@ -0,0 +1,23 @@ +package bank + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/ibc" +) + +// TODO: Codespace will be removed +const DefaultCodespace = 65534 + +type Keeper struct { + bk bank.Keeper + ch ibc.Channel +} + +func NewKeeper(key sdk.StoreKey, bk bank.Keeper, ibck ibc.Keeper) Keeper { + return Keeper{ + bk: bk, + // Prefixing for the future compatibility + ch: ibck.Channel(sdk.NewPrefixStoreGetter(key, []byte{0x00})), + } +} diff --git a/x/ibc/bank/types.go b/x/ibc/bank/types.go new file mode 100644 index 000000000000..64a3e1584a9b --- /dev/null +++ b/x/ibc/bank/types.go @@ -0,0 +1,43 @@ +package bank + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/ibc" +) + +type PayloadCoins struct { + SrcAddr sdk.AccAddress `json:"src-addr"` + DestAddr sdk.AccAddress `json:"dest-addr"` + Coins sdk.Coins `json:"coins"` +} + +func (p PayloadCoins) Type() string { + return "ibc/bank" +} + +func (p PayloadCoins) ValidateBasic() sdk.Error { + if !p.Coins.IsValid() { + return sdk.ErrInvalidCoins(p.Coins.String()) + } + if !p.Coins.IsPositive() { + return sdk.ErrInvalidCoins(p.Coins.String()) + } + return nil +} + +func (p PayloadCoins) DatagramType() ibc.DatagramType { + return ibc.PacketType +} + +func (p PayloadCoins) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{p.SrcAddr} +} + +type PayloadCoinsFail struct { + PayloadCoins +} + +func (p PayloadCoinsFail) DatagramType() ibc.DatagramType { + return ibc.ReceiptType +} diff --git a/x/ibc/channel.go b/x/ibc/channel.go new file mode 100644 index 000000000000..498185e9c556 --- /dev/null +++ b/x/ibc/channel.go @@ -0,0 +1,117 @@ +package ibc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/lib" + "github.com/cosmos/cosmos-sdk/wire" +) + +// ------------------------------------------ +// Type Definitions + +type Channel struct { + k Keeper + key sdk.KVStoreGetter +} + +func (k Keeper) Channel(key sdk.KVStoreGetter) Channel { + return Channel{ + k: k, + key: key, + } +} + +type DatagramType byte + +const ( + PacketType = DatagramType(iota) + ReceiptType +) + +type Header struct { + SrcChain string + DestChain string +} + +func (h Header) InverseDirection() Header { + return Header{ + SrcChain: h.DestChain, + DestChain: h.SrcChain, + } +} + +type Payload interface { + Type() string + ValidateBasic() sdk.Error + GetSigners() []sdk.AccAddress + DatagramType() DatagramType +} + +type Datagram struct { + Header + // Should we unexport Payload to possible modification from the modules? + Payload +} + +type Proof struct { + Height uint64 + Sequence uint64 +} + +// ------------------------------------------- +// Store Accessors + +func OutgoingQueuePrefix(ty DatagramType, chainid string) []byte { + return append(append([]byte{0x00}, byte(ty)), []byte(chainid)...) +} + +func outgoingQueue(store sdk.KVStore, cdc *wire.Codec, ty DatagramType, chainid string) lib.Linear { + return lib.NewLinear(cdc, store.Prefix(OutgoingQueuePrefix(ty, chainid)), nil) +} + +func IncomingSequenceKey(ty DatagramType, chainid string) []byte { + return append(append([]byte{0x01}, byte(ty)), []byte(chainid)...) +} + +func incomingSequence(store sdk.KVStore, cdc *wire.Codec, ty DatagramType, chainid string) lib.Value { + return lib.NewValue(store, cdc, IncomingSequenceKey(ty, chainid)) +} + +// -------------------------------------------- +// Channel Runtime + +type channelRuntime struct { + ch Channel + outgoingQueue lib.Queue + incomingSequence lib.Value + thisChain string + thatChain string +} + +func (ch Channel) runtime(ctx sdk.Context, ty DatagramType, thatChain string) channelRuntime { + store := ctx.KVStore(ch.k.key) + + return channelRuntime{ + ch: ch, + outgoingQueue: outgoingQueue(store, ch.k.cdc, ty, thatChain), + incomingSequence: incomingSequence(store, ch.k.cdc, ty, thatChain), + thisChain: ctx.ChainID(), + thatChain: thatChain, + } +} + +func (r channelRuntime) pushOutgoingQueue(data Datagram) { + r.outgoingQueue.Push(data) +} + +func (r channelRuntime) getIncomingSequence() (res uint64) { + ok := r.incomingSequence.Get(&res) + if !ok { + return 0 + } + return +} + +func (r channelRuntime) setIncomingSequence(seq uint64) { + r.incomingSequence.Set(seq) +} diff --git a/x/ibc/channel_handler.go b/x/ibc/channel_handler.go new file mode 100644 index 000000000000..c3e205819b99 --- /dev/null +++ b/x/ibc/channel_handler.go @@ -0,0 +1,110 @@ +package ibc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type SendHandler func(Payload) sdk.Result + +func (ch Channel) Send(h SendHandler, ctx sdk.Context, msg MsgSend) (result sdk.Result) { + payload := msg.Payload + r := ch.runtime(ctx, payload.DatagramType(), msg.DestChain) + + // TODO: check validity of the payload; the module have to be permitted to send the payload + result = h(msg.Payload) + if !result.IsOK() { + return + } + + data := Datagram{ + Header: Header{ + SrcChain: ctx.ChainID(), + DestChain: msg.DestChain, + }, + Payload: payload, + } + r.pushOutgoingQueue(data) + + return +} + +type ReceiveHandler func(sdk.Context, Payload) (Payload, sdk.Result) + +func (ch Channel) Receive(h ReceiveHandler, ctx sdk.Context, msg MsgReceive) (res sdk.Result) { + data := msg.Datagram + payload := data.Payload + ty := payload.DatagramType() + chr := ch.runtime(ctx, ty, msg.SrcChain) + connr := ch.k.runtime(ctx, msg.SrcChain) + + prf := msg.Proof + destChain := msg.Datagram.Header.DestChain + + if !connr.connEstablished() { + return ErrConnectionNotEstablished(ch.k.codespace).Result() + } + + if ctx.ChainID() != destChain { + return ErrChainMismatch(ch.k.codespace).Result() + } + + // TODO: verify merkle proof + + seq := chr.getIncomingSequence() + if seq != prf.Sequence { + return ErrInvalidSequence(ch.k.codespace).Result() + } + chr.setIncomingSequence(seq + 1) + + switch ty { + case PacketType: + return receivePacket(h, ctx, chr, data) + case ReceiptType: + return receiveReceipt(h, ctx, chr, data) + default: + // Source zone sent invalid datagram, reorg needed + return ErrUnknownDatagramType(ch.k.codespace).Result() + } +} + +func receivePacket(h ReceiveHandler, ctx sdk.Context, r channelRuntime, data Datagram) (res sdk.Result) { + // Packet handling can fail + // If fails, reverts all execution done by DatagramHandler + + cctx, write := ctx.CacheContext() + receipt, res := h(cctx, data.Payload) + if receipt != nil { + newdata := Datagram{ + Header: data.Header.InverseDirection(), + Payload: receipt, + } + + r.pushOutgoingQueue(newdata) + } + if !res.IsOK() { + return WrapResult(res) + } + write() + + return +} + +func receiveReceipt(h ReceiveHandler, ctx sdk.Context, r channelRuntime, data Datagram) (res sdk.Result) { + // Receipt handling should not fail + + receipt, res := h(ctx, data.Payload) + if !res.IsOK() { + panic("IBC Receipt handler should not fail") + } + if receipt != nil { + panic("IBC Receipt handler cannot return new receipt") + } + + return +} + +/* +func cleanup(store sdk.KVStore, cdc *wire.Codec, ty DatagramType, srcChain string) sdk.Result { + queue := outgoingQueue(store, cdc, ty, srcChain) +} +*/ diff --git a/x/ibc/channel_msgs.go b/x/ibc/channel_msgs.go new file mode 100644 index 000000000000..a035e67e186c --- /dev/null +++ b/x/ibc/channel_msgs.go @@ -0,0 +1,64 @@ +package ibc + +import ( + "encoding/json" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ---------------------------------------- +// Msg Definitions + +// MsgSend is a simpler form of sending ibc payload to another chain +// when there is no need for preparation +type MsgSend struct { + Payload + DestChain string +} + +func (msg MsgSend) GetSignBytes() []byte { + bz, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return bz +} + +// MsgReceive defines the message that a relayer uses to post a packet +// to the destination chain. +type MsgReceive struct { + Datagram + Proof + Relayer sdk.AccAddress +} + +func (msg MsgReceive) GetSignBytes() []byte { + bz, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return bz +} + +func (msg MsgReceive) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Relayer} +} + +type MsgCleanup struct { + Sequence int64 + SrcChain string + Proof Proof + Cleaner sdk.AccAddress +} + +func (msg MsgCleanup) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Cleaner} +} + +func (msg MsgCleanup) Type() string { + return "ibc" +} + +func (msg MsgCleanup) ValidateBasic() sdk.Error { + return nil +} diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 92b03a66fcbb..d156c77d9c67 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/lib" wire "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -21,10 +22,10 @@ import ( // flags const ( - FlagFromChainID = "from-chain-id" - FlagFromChainNode = "from-chain-node" - FlagToChainID = "to-chain-id" - FlagToChainNode = "to-chain-node" + FlagSrcChainID = "src-chain-id" + FlagSrcChainNode = "src-chain-node" + FlagDestChainID = "dest-chain-id" + FlagDestChainNode = "dest-chain-node" ) type relayCommander struct { @@ -55,103 +56,90 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command { Run: cmdr.runIBCRelay, } - cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets") - cmd.Flags().String(FlagFromChainNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") - cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets") - cmd.Flags().String(FlagToChainNode, "tcp://localhost:36657", ": to tendermint rpc interface for this chain") + cmd.Flags().String(FlagSrcChainID, "", "Chain ID for ibc node to check outgoing packets") + cmd.Flags().String(FlagSrcChainNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") + cmd.Flags().String(FlagDestChainID, "", "Chain ID for ibc node to broadcast incoming packets") + cmd.Flags().String(FlagDestChainNode, "tcp://localhost:36657", ": to tendermint rpc interface for this chain") - cmd.MarkFlagRequired(FlagFromChainID) - cmd.MarkFlagRequired(FlagFromChainNode) - cmd.MarkFlagRequired(FlagToChainID) - cmd.MarkFlagRequired(FlagToChainNode) + cmd.MarkFlagRequired(FlagSrcChainID) + cmd.MarkFlagRequired(FlagSrcChainNode) + cmd.MarkFlagRequired(FlagDestChainID) + cmd.MarkFlagRequired(FlagDestChainNode) - viper.BindPFlag(FlagFromChainID, cmd.Flags().Lookup(FlagFromChainID)) - viper.BindPFlag(FlagFromChainNode, cmd.Flags().Lookup(FlagFromChainNode)) - viper.BindPFlag(FlagToChainID, cmd.Flags().Lookup(FlagToChainID)) - viper.BindPFlag(FlagToChainNode, cmd.Flags().Lookup(FlagToChainNode)) + viper.BindPFlag(FlagSrcChainID, cmd.Flags().Lookup(FlagSrcChainID)) + viper.BindPFlag(FlagSrcChainNode, cmd.Flags().Lookup(FlagSrcChainNode)) + viper.BindPFlag(FlagDestChainID, cmd.Flags().Lookup(FlagDestChainID)) + viper.BindPFlag(FlagDestChainNode, cmd.Flags().Lookup(FlagDestChainNode)) return cmd } // nolint: unparam func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { - fromChainID := viper.GetString(FlagFromChainID) - fromChainNode := viper.GetString(FlagFromChainNode) - toChainID := viper.GetString(FlagToChainID) - toChainNode := viper.GetString(FlagToChainNode) - - address, err := context.NewCLIContext().GetFromAddress() + srcChainID := viper.GetString(FlagSrcChainID) + srcChainNode := viper.GetString(FlagSrcChainNode) + toChainID := viper.GetString(FlagDestChainID) + toChainNode := viper.GetString(FlagDestChainNode) + address, err := context.NewCoreContextFromViper().GetFromAddress() if err != nil { panic(err) } c.address = address - c.loop(fromChainID, fromChainNode, toChainID, toChainNode) + ctx := context.NewCoreContextFromViper() + // TODO: use proper config + egressQueue := lib.NewLinearClient(ctx.WithNodeURI(srcChainNode), "bank", c.cdc, []byte("ibc/"), nil) + c.loop(egressQueue, srcChainID, toChainID, toChainNode) } -// This is nolinted as someone is in the process of refactoring this to remove the goto -// nolint: gocyclo -func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { - cliCtx := context.NewCLIContext() - - passphrase, err := keys.ReadPassphraseFromStdin(cliCtx.FromAddressName) +func (c relayCommander) processed(node string, srcChainID string) uint64 { + // TODO: Support receipts + bz, err := query(node, ibc.IncomingSequenceKey(ibc.PacketType, srcChainID), c.ibcStore) if err != nil { panic(err) } - ingressKey := ibc.IngressSequenceKey(fromChainID) - -OUTER: - for { - time.Sleep(5 * time.Second) - - processedbz, err := query(toChainNode, ingressKey, c.ibcStore) - if err != nil { - panic(err) - } + if bz == nil { + return 0 + } + var res int64 + c.cdc.MustUnmarshalBinary(bz, &res) - var processed int64 - if processedbz == nil { - processed = 0 - } else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil { - panic(err) - } + if res < 0 { + panic("Negative processed result") + } - lengthKey := ibc.EgressLengthKey(toChainID) - egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore) - if err != nil { - c.logger.Error("error querying outgoing packet list length", "err", err) - continue OUTER //TODO replace with continue (I think it should just to the correct place where OUTER is now) - } + return uint64(res) +} - var egressLength int64 - if egressLengthbz == nil { - egressLength = 0 - } else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil { - panic(err) - } +// This is nolinted as someone is in the process of refactoring this to remove the goto +// nolint: gocyclo +func (c relayCommander) loop(egressQueue lib.LinearClient, srcChainID, chainID, chainNode string) { + ctx := context.NewCoreContextFromViper() + for { + time.Sleep(5 * time.Second) - if egressLength > processed { - c.logger.Info("Detected IBC packet", "number", egressLength-1) + proc := c.processed(chainNode, srcChainID) + length := egressQueue.Len() + if length <= proc { + continue } - seq := c.getSequence(toChainNode) + c.logger.Info("Detected IBC packet", "number", length-1) - for i := processed; i < egressLength; i++ { - egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore) + var data ibc.Datagram + for i := proc; i < length; i++ { + err := egressQueue.Get(i, &data) if err != nil { - c.logger.Error("error querying egress packet", "err", err) - continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER) + panic(err) } - err = c.broadcastTx(seq, toChainNode, c.refine(egressbz, i, passphrase)) - - seq++ - + // TODO: add proof + msg := ibc.MsgReceive{Datagram: data, Relayer: c.address} + err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, c.cdc) if err != nil { - c.logger.Error("error broadcasting ingress packet", "err", err) - continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER) + panic(err) } c.logger.Info("Relayed IBC packet", "number", i) @@ -185,26 +173,3 @@ func (c relayCommander) getSequence(node string) int64 { return 0 } - -func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte { - var packet ibc.IBCPacket - if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil { - panic(err) - } - - msg := ibc.IBCReceiveMsg{ - IBCPacket: packet, - Relayer: c.address, - Sequence: sequence, - } - - txCtx := authctx.NewTxContextFromCLI().WithSequence(sequence).WithCodec(c.cdc) - cliCtx := context.NewCLIContext() - - res, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, []sdk.Msg{msg}) - if err != nil { - panic(err) - } - - return res -} diff --git a/x/ibc/conn.go b/x/ibc/conn.go new file mode 100644 index 000000000000..4569e811979c --- /dev/null +++ b/x/ibc/conn.go @@ -0,0 +1,91 @@ +package ibc + +import ( + "github.com/tendermint/tendermint/lite" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/lib" + "github.com/cosmos/cosmos-sdk/wire" +) + +// ------------------------------------------ +// Type Definitions + +// Keeper manages conn between chains +type Keeper struct { + key sdk.StoreKey + cdc *wire.Codec + + codespace sdk.CodespaceType +} + +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Keeper { + return Keeper{ + key: key, + cdc: cdc, + + codespace: codespace, + } +} + +// ----------------------------------------- +// Store Accessors + +func CommitHeightKey(srcChain string) []byte { + return append([]byte{0x00}, []byte(srcChain)...) +} + +func commitHeight(store sdk.KVStore, cdc *wire.Codec, srcChain string) lib.Value { + return lib.NewValue(store, cdc, CommitHeightKey(srcChain)) +} + +func CommitListPrefix(srcChain string) []byte { + return append([]byte{0x01}, []byte(srcChain)...) +} + +func commitList(store sdk.KVStore, cdc *wire.Codec, srcChain string) lib.List { + return lib.NewList(cdc, store.Prefix(CommitListPrefix(srcChain)), nil) +} + +// -------------------------------------- +// Keeper Runtime + +type connRuntime struct { + k Keeper + height lib.Value + commits lib.List +} + +func (k Keeper) runtime(ctx sdk.Context, srcChain string) connRuntime { + store := ctx.KVStore(k.key) + return connRuntime{ + k: k, + height: commitHeight(store, k.cdc, srcChain), + commits: commitList(store, k.cdc, srcChain), + } +} + +func (r connRuntime) connEstablished() (established bool) { + return r.height.Has() +} + +func (r connRuntime) getCommitHeight() (height uint64) { + r.height.MustGet(&height) + return +} + +func (r connRuntime) setCommitHeight(height uint64) { + r.height.Set(height) +} + +func (r connRuntime) getCommit(height uint64) (commit lite.FullCommit) { + err := r.commits.Get(height, &commit) + if err != nil { + panic(err) + } + return +} + +func (r connRuntime) setCommit(height uint64, commit lite.FullCommit) { + r.commits.Set(height, commit) +} diff --git a/x/ibc/conn_handler.go b/x/ibc/conn_handler.go new file mode 100644 index 000000000000..71c779e82c58 --- /dev/null +++ b/x/ibc/conn_handler.go @@ -0,0 +1,63 @@ +package ibc + +import ( + "reflect" + + 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 MsgOpenConnection: + return handleMsgOpenConnection(ctx, k, msg) + case MsgUpdateConnection: + return handleMsgUpdateConnection(ctx, k, msg) + default: + errMsg := "Unrecognized IBC Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +func handleMsgOpenConnection(ctx sdk.Context, k Keeper, msg MsgOpenConnection) sdk.Result { + r := k.runtime(ctx, msg.SrcChain) + + if r.connEstablished() { + return ErrConnectionAlreadyEstablished(k.codespace).Result() + } + + height := uint64(msg.ROT.Height()) + r.setCommitHeight(height) + r.setCommit(height, msg.ROT) + + return sdk.Result{} +} + +func handleMsgUpdateConnection(ctx sdk.Context, k Keeper, msg MsgUpdateConnection) sdk.Result { + r := k.runtime(ctx, msg.SrcChain) + + if !r.connEstablished() { + return ErrConnectionNotEstablished(k.codespace).Result() + } + + lastheight := r.getCommitHeight() + height := uint64(msg.Commit.Commit.Height()) + if height < lastheight { + return ErrInvalidHeight(k.codespace).Result() + } + + // TODO: add lc verificatioon + /* + lastcommit := r.getCommit(lastheight) + + cert := lite.NewDynamicCertifier(msg.SrcChain, commit.Validators, height) + if err := cert.Update(msg.Commit); err != nil { + return ErrUpdateCommitFailed(k.codespace, err).Result() + } + + k.setCommit(ctx, msg.SrcChain, msg.Commit.Height(), msg.Commit) + */ + r.setCommit(height, msg.Commit) + return sdk.Result{} +} diff --git a/x/ibc/conn_msgs.go b/x/ibc/conn_msgs.go new file mode 100644 index 000000000000..413e20d8b200 --- /dev/null +++ b/x/ibc/conn_msgs.go @@ -0,0 +1,72 @@ +package ibc + +import ( + "encoding/json" + + "github.com/tendermint/tendermint/lite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// MsgOpenConnection defines the message that is used for open a c +// that receives msg from another chain +type MsgOpenConnection struct { + ROT lite.FullCommit + SrcChain string + Signer sdk.AccAddress +} + +func (msg MsgOpenConnection) Type() string { + return "ibc" +} + +func (msg MsgOpenConnection) GetSignBytes() []byte { + bz, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return bz +} + +func (msg MsgOpenConnection) ValidateBasic() sdk.Error { + if msg.ROT.Height() < 0 { + // XXX: Codespace will be removed + return ErrInvalidHeight(111) + } + return nil +} + +func (msg MsgOpenConnection) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} + +type MsgUpdateConnection struct { + SrcChain string + Commit lite.FullCommit + //PacketProof + Signer sdk.AccAddress +} + +func (msg MsgUpdateConnection) Type() string { + return "ibc" +} + +func (msg MsgUpdateConnection) GetSignBytes() []byte { + bz, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return bz +} + +func (msg MsgUpdateConnection) ValidateBasic() sdk.Error { + if msg.Commit.Commit.Height() < 0 { + // XXX: Codespace will be removed + return ErrInvalidHeight(111) + } + return nil +} + +func (msg MsgUpdateConnection) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} diff --git a/x/ibc/errors.go b/x/ibc/errors.go index 7a3194baf1ff..61a7ce2cfcae 100644 --- a/x/ibc/errors.go +++ b/x/ibc/errors.go @@ -9,30 +9,107 @@ const ( DefaultCodespace sdk.CodespaceType = 3 // IBC errors reserve 200 - 299. - CodeInvalidSequence sdk.CodeType = 200 - CodeIdenticalChains sdk.CodeType = 201 - CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest + CodeConnectionNotEstablished sdk.CodeType = 200 + CodeConnectionAlreadyEstablished sdk.CodeType = 201 + CodeChainMismatch sdk.CodeType = 202 + CodeChannelNotOpened sdk.CodeType = 203 + CodeChannelAlreadyOpened sdk.CodeType = 204 + CodeInvalidProof sdk.CodeType = 205 + CodeInvalidPacket sdk.CodeType = 206 + CodeUnknownDatagramType sdk.CodeType = 207 + + /* + + CodeIdenticalChains sdk.CodeType = 201 + CodeChainMismatch sdk.CodeType = 202 + CodeUpdateCommitFailed sdk.CodeType = 205 + CodeInvalidPacket sdk.CodeType = 206 + CodeNoCommitFound sdk.CodeType = 207 + CodeUnauthorizedSend sdk.CodeType = 208 + CodeUnauthorizedSendReceipt sdk.CodeType = 209 + */ + CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest ) func codeToDefaultMsg(code sdk.CodeType) string { switch code { - case CodeInvalidSequence: - return "invalid IBC packet sequence" - case CodeIdenticalChains: - return "source and destination chain cannot be identical" + /* case CodeInvalidSequence: + return "Invalid IBC packet sequence" + case CodeIdenticalChains: + return "Source and destination chain cannot be identical" + case CodeChainMismatch: + return "Destination chain is not current chain"*/ default: return sdk.CodeToDefaultMsg(code) } } // nolint +func ErrConnectionNotEstablished(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeConnectionNotEstablished, "") +} + +func ErrConnectionAlreadyEstablished(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeConnectionAlreadyEstablished, "") +} + +func ErrChannelNotOpened(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeChannelNotOpened, "") +} + +func ErrChannelAlreadyOpened(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeChannelAlreadyOpened, "") +} + +func ErrChainMismatch(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeChainMismatch, "") +} + func ErrInvalidSequence(codespace sdk.CodespaceType) sdk.Error { - return newError(codespace, CodeInvalidSequence, "") + return newError(codespace, CodeInvalidProof, "Invalid sequence") +} + +func ErrInvalidHeight(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeInvalidPacket, "Invalid height") +} + +func ErrUnknownDatagramType(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeUnknownDatagramType, "") } + +/* + + +// nolint + func ErrIdenticalChains(codespace sdk.CodespaceType) sdk.Error { return newError(codespace, CodeIdenticalChains, "") } + + + + +func ErrUpdateCommitFailed(codespace sdk.CodespaceType, err error) sdk.Error { + return newError(codespace, CodeUpdateCommitFailed, err.Error()) +} + +func ErrInvalidPacket(codespace sdk.CodespaceType, err error) sdk.Error { + return newError(codespace, CodeInvalidPacket, err.Error()) +} + +func ErrNoCommitFound(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeNoCommitFound, "") +} + +func ErrUnauthorizedSend(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeUnauthorizedSend, "") +} + +func ErrUnauthorizedSendReceipt(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeUnauthorizedSendReceipt, "") +} +*/ // ------------------------- // Helpers diff --git a/x/ibc/handler.go b/x/ibc/handler.go deleted file mode 100644 index 1f334166bfff..000000000000 --- a/x/ibc/handler.go +++ /dev/null @@ -1,58 +0,0 @@ -package ibc - -import ( - "reflect" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank" -) - -func NewHandler(ibcm Mapper, ck bank.Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case IBCTransferMsg: - return handleIBCTransferMsg(ctx, ibcm, ck, msg) - case IBCReceiveMsg: - return handleIBCReceiveMsg(ctx, ibcm, ck, msg) - default: - errMsg := "Unrecognized IBC Msg type: " + reflect.TypeOf(msg).Name() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// IBCTransferMsg deducts coins from the account and creates an egress IBC packet. -func handleIBCTransferMsg(ctx sdk.Context, ibcm Mapper, ck bank.Keeper, msg IBCTransferMsg) sdk.Result { - packet := msg.IBCPacket - - _, _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) - if err != nil { - return err.Result() - } - - err = ibcm.PostIBCPacket(ctx, packet) - if err != nil { - return err.Result() - } - - return sdk.Result{} -} - -// IBCReceiveMsg adds coins to the destination address and creates an ingress IBC packet. -func handleIBCReceiveMsg(ctx sdk.Context, ibcm Mapper, ck bank.Keeper, msg IBCReceiveMsg) sdk.Result { - packet := msg.IBCPacket - - seq := ibcm.GetIngressSequence(ctx, packet.SrcChain) - if msg.Sequence != seq { - return ErrInvalidSequence(ibcm.codespace).Result() - } - - _, _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) - if err != nil { - return err.Result() - } - - ibcm.SetIngressSequence(ctx, packet.SrcChain, seq+1) - - return sdk.Result{} -} diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 88718b4a2273..6d3877c00d4b 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -1,6 +1,7 @@ package ibc import ( + "os" "testing" "github.com/stretchr/testify/require" @@ -9,20 +10,26 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/lite" + tmtypes "github.com/tendermint/tendermint/types" + bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" ) +var testCodespace = sdk.CodespaceUndefined + // AccountMapper(/Keeper) and IBCMapper should use different StoreKey later -func defaultContext(key sdk.StoreKey) sdk.Context { +func defaultContext(keys ...sdk.StoreKey) sdk.Context { db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + for _, key := range keys { + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + } cms.LoadLatestVersion() ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) return ctx @@ -32,10 +39,45 @@ func newAddress() sdk.AccAddress { return sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) } -func getCoins(ck bank.Keeper, ctx sdk.Context, addr sdk.AccAddress) (sdk.Coins, sdk.Error) { - zero := sdk.Coins(nil) - coins, _, err := ck.AddCoins(ctx, addr, zero) - return coins, err +type remoteSavePayload struct { + key []byte + value []byte +} + +func (p remoteSavePayload) Type() string { + return "remote" +} + +func (p remoteSavePayload) ValidateBasic() sdk.Error { + return nil +} + +func (p remoteSavePayload) DatagramType() DatagramType { + return PacketType +} + +func (p remoteSavePayload) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{} +} + +type remoteSaveFailPayload struct { + remoteSavePayload +} + +func (p remoteSaveFailPayload) Type() string { + return "remote" +} + +func (p remoteSaveFailPayload) ValidateBasic() sdk.Error { + return nil +} + +func (p remoteSaveFailPayload) DatagramType() DatagramType { + return ReceiptType +} + +func (p remoteSaveFailPayload) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{} } func makeCodec() *wire.Codec { @@ -43,94 +85,228 @@ func makeCodec() *wire.Codec { // Register Msgs cdc.RegisterInterface((*sdk.Msg)(nil), nil) - cdc.RegisterConcrete(bank.MsgSend{}, "test/ibc/Send", nil) - cdc.RegisterConcrete(bank.MsgIssue{}, "test/ibc/Issue", nil) - cdc.RegisterConcrete(IBCTransferMsg{}, "test/ibc/IBCTransferMsg", nil) - cdc.RegisterConcrete(IBCReceiveMsg{}, "test/ibc/IBCReceiveMsg", nil) + cdc.RegisterConcrete(MsgSend{}, "test/ibc/Send", nil) + cdc.RegisterConcrete(MsgReceive{}, "test/ibc/Receive", nil) - // Register AppAccount - cdc.RegisterInterface((*auth.Account)(nil), nil) - cdc.RegisterConcrete(&auth.BaseAccount{}, "test/ibc/Account", nil) - wire.RegisterCrypto(cdc) + // Register Payloads + cdc.RegisterInterface((*Payload)(nil), nil) + cdc.RegisterConcrete(remoteSavePayload{}, "test/payload/remoteSave", nil) + cdc.RegisterConcrete(remoteSaveFailPayload{}, "test/payload/remoteSaveFail", nil) cdc.Seal() return cdc + } -func TestIBC(t *testing.T) { +func newIBCTestApp(logger log.Logger, db dbm.DB) *bam.BaseApp { cdc := makeCodec() + app := bam.NewBaseApp("test", cdc, logger, db) - key := sdk.NewKVStoreKey("ibc") - ctx := defaultContext(key) - - am := auth.NewAccountMapper(cdc, key, auth.ProtoBaseAccount) - ck := bank.NewKeeper(am) - - src := newAddress() - dest := newAddress() - chainid := "ibcchain" - zero := sdk.Coins(nil) - mycoins := sdk.Coins{sdk.NewInt64Coin("mycoin", 10)} - - coins, _, err := ck.AddCoins(ctx, src, mycoins) - require.Nil(t, err) - require.Equal(t, mycoins, coins) - - ibcm := NewMapper(cdc, key, DefaultCodespace) - h := NewHandler(ibcm, ck) - packet := IBCPacket{ - SrcAddr: src, - DestAddr: dest, - Coins: mycoins, - SrcChain: chainid, - DestChain: chainid, + key := sdk.NewKVStoreKey("remote") + ibcKey := sdk.NewKVStoreKey("ibc") + keeper := NewKeeper(cdc, ibcKey, app.RegisterCodespace(DefaultCodespace)) + + app.Router(). + AddRoute("remote", remoteSaveHandler(key, keeper)). + AddRoute("ibc", NewHandler(keeper)) + + app.MountStoresIAVL(key, ibcKey) + err := app.LoadLatestVersion(key) + if err != nil { + panic(err) } + app.InitChain(abci.RequestInitChain{}) + return app +} + +func remoteSaveHandler(key sdk.StoreKey, ibck Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ibcc := ibck.Channel(sdk.NewPrefixStoreGetter(key, []byte("ibctest"))) + switch msg := msg.(type) { + case MsgSend: + return ibcc.Send(func(p Payload) sdk.Result { + switch p := p.(type) { + case remoteSavePayload: + return handleRemoteSavePayloadSend(p) + default: + return sdk.ErrUnknownRequest("").Result() + } + }, ctx, msg) + case MsgReceive: + return ibcc.Receive(func(ctx sdk.Context, p Payload) (Payload, sdk.Result) { + switch p := p.(type) { + case remoteSavePayload: + return handleRemoteSavePayloadReceive(ctx, key, p) + case remoteSaveFailPayload: + return handleRemoteSaveFailPayloadReceive(ctx, key, p) + default: + return nil, sdk.ErrUnknownRequest("").Result() + } + }, ctx, msg) + /* + case MsgCleanup: + return ibcc.Cleanup() + */ + default: + return sdk.ErrUnknownRequest("").Result() + } + } +} + +func handleRemoteSavePayloadSend(p Payload) sdk.Result { + return sdk.Result{} +} + +func handleRemoteSavePayloadReceive(ctx sdk.Context, key sdk.StoreKey, p remoteSavePayload) (Payload, sdk.Result) { store := ctx.KVStore(key) + if store.Has(p.key) { + return remoteSaveFailPayload{p}, sdk.NewError(testCodespace, 1000, "Key already exists").Result() + } + store.Set(p.key, p.value) + return nil, sdk.Result{} +} + +func handleRemoteSaveFailPayloadReceive(ctx sdk.Context, key sdk.StoreKey, p remoteSaveFailPayload) (Payload, sdk.Result) { + return nil, sdk.Result{} +} + +func TestIBC(t *testing.T) { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") + db := dbm.NewMemDB() + app := newIBCTestApp(logger, db) + + ctx := app.NewContext(false, abci.Header{}) + chainid := ctx.ChainID() - var msg sdk.Msg var res sdk.Result - var egl int64 - var igs int64 - egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, int64(0)) + // Open connection + openConnMsg := MsgOpenConnection{ + ROT: lite.FullCommit{ + Commit: lite.Commit{ + Header: &tmtypes.Header{ + Height: 1, + }, + }, + }, + } + + tx := auth.NewStdTx([]sdk.Msg{openConnMsg}, auth.NewStdFee(0), []auth.StdSignature{}, "") - msg = IBCTransferMsg{ - IBCPacket: packet, + res = app.Deliver(tx) + require.True(t, res.IsOK(), "%+v", res) + + // Open channel + + // Send IBC message + payload := remoteSavePayload{ + key: []byte("hello"), + value: []byte("world"), } - res = h(ctx, msg) + + saveMsg := MsgSend{ + Payload: payload, + DestChain: chainid, + } + + tx.Msgs[0] = saveMsg + + res = app.Deliver(tx) + require.True(t, res.IsOK(), "%+v", res) + + // Receive IBC message + data := Datagram{ + Header: Header{ + SrcChain: chainid, + DestChain: chainid, + }, + Payload: payload, + } + + receiveMsg := MsgReceive{ + Datagram: data, + /* PacketProof: PacketProof{ + Sequence: 0, + },*/ + Relayer: newAddress(), + } + + tx.Msgs[0] = receiveMsg + + res = app.Deliver(tx) + require.True(t, res.IsOK(), "%+v\n", res) + /* + store := ctx.KVStore(key) + val := store.Get(payload.key) + require.Equal(t, payload.value, val) + */ + + tx.Msgs[0] = receiveMsg + res = app.Deliver(tx) + require.False(t, UnwrapResult(res).IsOK(), "%+v\n", res) + + // Send another IBC message and receive it + // It has duplicated key bytes so fails + tx.Msgs[0] = saveMsg + res = app.Deliver(tx) require.True(t, res.IsOK()) - coins, err = getCoins(ck, ctx, src) - require.Nil(t, err) - require.Equal(t, zero, coins) + receiveMsg = MsgReceive{ + Datagram: data, + /*PacketProof: PacketProof{ + Sequence: 1, + },*/ + Relayer: newAddress(), + } - egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, int64(1)) + tx.Msgs[0] = receiveMsg + res = app.Deliver(tx) + require.False(t, UnwrapResult(res).IsOK()) - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(0)) + // Return fail receipt + data.Payload = remoteSaveFailPayload{payload} - msg = IBCReceiveMsg{ - IBCPacket: packet, - Relayer: src, - Sequence: 0, + receiptMsg := MsgReceive{ + Datagram: data, + /*PacketProof: PacketProof{ + Sequence: 0, + },*/ + Relayer: newAddress(), } - res = h(ctx, msg) + + tx.Msgs[0] = receiptMsg + res = app.Deliver(tx) require.True(t, res.IsOK()) + /* + // Cleanup receive queue + receiveCleanupMsg := MsgReceiveCleanup{ + Sequence: 2, + SrcChain: chainid, + //CleanupProof: CleanupProof{}, + Cleaner: newAddress(), + } - coins, err = getCoins(ck, ctx, dest) - require.Nil(t, err) - require.Equal(t, mycoins, coins) + tx.Msgs[0] = receiveCleanupMsg + res = app.Deliver(tx) + require.True(t, res.IsOK(), "%+v", res) - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(1)) + // Cleanup receipt queue + receiptCleanupMsg := MsgReceiptCleanup{ + Sequence: 1, + SrcChain: chainid, + //CleanupProof: CleanupProof{}, + Cleaner: newAddress(), + } - res = h(ctx, msg) - require.False(t, res.IsOK()) + tx.Msgs[0] = receiptCleanupMsg + res = app.Deliver(tx) + require.True(t, UnwrapResult(res).IsOK()) - igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(1)) + unknownMsg := sdk.NewTestMsg(newAddress()) + tx.Msgs[0] = unknownMsg + res = app.Deliver(tx) + require.False(t, UnwrapResult(res).IsOK()) + */ } diff --git a/x/ibc/mapper.go b/x/ibc/mapper.go deleted file mode 100644 index 25169961708d..000000000000 --- a/x/ibc/mapper.go +++ /dev/null @@ -1,130 +0,0 @@ -package ibc - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - wire "github.com/cosmos/cosmos-sdk/wire" -) - -// IBC Mapper -type Mapper struct { - key sdk.StoreKey - cdc *wire.Codec - codespace sdk.CodespaceType -} - -// XXX: The Mapper should not take a CoinKeeper. Rather have the CoinKeeper -// take an Mapper. -func NewMapper(cdc *wire.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) Mapper { - // XXX: How are these codecs supposed to work? - return Mapper{ - key: key, - cdc: cdc, - codespace: codespace, - } -} - -// XXX: This is not the public API. This will change in MVP2 and will henceforth -// only be invoked from another module directly and not through a user -// transaction. -// TODO: Handle invalid IBC packets and return errors. -func (ibcm Mapper) PostIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { - // write everything into the state - store := ctx.KVStore(ibcm.key) - index := ibcm.getEgressLength(store, packet.DestChain) - bz, err := ibcm.cdc.MarshalBinary(packet) - if err != nil { - panic(err) - } - - store.Set(EgressKey(packet.DestChain, index), bz) - bz, err = ibcm.cdc.MarshalBinary(index + 1) - if err != nil { - panic(err) - } - store.Set(EgressLengthKey(packet.DestChain), bz) - - return nil -} - -// XXX: In the future every module is able to register it's own handler for -// handling it's own IBC packets. The "ibc" handler will only route the packets -// to the appropriate callbacks. -// XXX: For now this handles all interactions with the CoinKeeper. -// XXX: This needs to do some authentication checking. -func (ibcm Mapper) ReceiveIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { - return nil -} - -// -------------------------- -// Functions for accessing the underlying KVStore. - -func marshalBinaryPanic(cdc *wire.Codec, value interface{}) []byte { - res, err := cdc.MarshalBinary(value) - if err != nil { - panic(err) - } - return res -} - -func unmarshalBinaryPanic(cdc *wire.Codec, bz []byte, ptr interface{}) { - err := cdc.UnmarshalBinary(bz, ptr) - if err != nil { - panic(err) - } -} - -// TODO add description -func (ibcm Mapper) GetIngressSequence(ctx sdk.Context, srcChain string) int64 { - store := ctx.KVStore(ibcm.key) - key := IngressSequenceKey(srcChain) - - bz := store.Get(key) - if bz == nil { - zero := marshalBinaryPanic(ibcm.cdc, int64(0)) - store.Set(key, zero) - return 0 - } - - var res int64 - unmarshalBinaryPanic(ibcm.cdc, bz, &res) - return res -} - -// TODO add description -func (ibcm Mapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence int64) { - store := ctx.KVStore(ibcm.key) - key := IngressSequenceKey(srcChain) - - bz := marshalBinaryPanic(ibcm.cdc, sequence) - store.Set(key, bz) -} - -// Retrieves the index of the currently stored outgoing IBC packets. -func (ibcm Mapper) getEgressLength(store sdk.KVStore, destChain string) int64 { - bz := store.Get(EgressLengthKey(destChain)) - if bz == nil { - zero := marshalBinaryPanic(ibcm.cdc, int64(0)) - store.Set(EgressLengthKey(destChain), zero) - return 0 - } - var res int64 - unmarshalBinaryPanic(ibcm.cdc, bz, &res) - return res -} - -// Stores an outgoing IBC packet under "egress/chain_id/index". -func EgressKey(destChain string, index int64) []byte { - return []byte(fmt.Sprintf("egress/%s/%d", destChain, index)) -} - -// Stores the number of outgoing IBC packets under "egress/index". -func EgressLengthKey(destChain string) []byte { - return []byte(fmt.Sprintf("egress/%s", destChain)) -} - -// Stores the sequence number of incoming IBC packet under "ingress/index". -func IngressSequenceKey(srcChain string) []byte { - return []byte(fmt.Sprintf("ingress/%s", srcChain)) -} diff --git a/x/ibc/result.go b/x/ibc/result.go new file mode 100644 index 000000000000..ee8d4c9ec99b --- /dev/null +++ b/x/ibc/result.go @@ -0,0 +1,46 @@ +package ibc + +import ( + "encoding/json" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// IBC handler returns OK if the inner transaction executed +// no matter it succeed or not +// so store the code in the data field + +type ResultData struct { + Code sdk.ABCICodeType + Data []byte +} + +func WrapResult(res1 sdk.Result) (res2 sdk.Result) { + res2 = res1 + res2.Code = sdk.ABCICodeOK + res2.Data = ResultData{res1.Code, res1.Data}.Marshal() + return +} + +func UnwrapResult(res1 sdk.Result) (res2 sdk.Result) { + res2 = res1 + var data ResultData + if (&data).Unmarshal(res1.Data) != nil { + return res1 + } + res2.Code = data.Code + res2.Data = data.Data + return +} + +func (data ResultData) Marshal() []byte { + bz, err := json.Marshal(data) + if err != nil { + panic(err) + } + return bz +} + +func (data *ResultData) Unmarshal(bz []byte) error { + return json.Unmarshal(bz, data) +} diff --git a/x/ibc/types.go b/x/ibc/types.go deleted file mode 100644 index 5f9ee611bb13..000000000000 --- a/x/ibc/types.go +++ /dev/null @@ -1,123 +0,0 @@ -package ibc - -import ( - "encoding/json" - - sdk "github.com/cosmos/cosmos-sdk/types" - wire "github.com/cosmos/cosmos-sdk/wire" -) - -var ( - msgCdc *wire.Codec -) - -func init() { - msgCdc = wire.NewCodec() -} - -// ------------------------------ -// IBCPacket - -// nolint - TODO rename to Packet as IBCPacket stutters (golint) -// IBCPacket defines a piece of data that can be send between two separate -// blockchains. -type IBCPacket struct { - SrcAddr sdk.AccAddress - DestAddr sdk.AccAddress - Coins sdk.Coins - SrcChain string - DestChain string -} - -func NewIBCPacket(srcAddr sdk.AccAddress, destAddr sdk.AccAddress, coins sdk.Coins, - srcChain string, destChain string) IBCPacket { - - return IBCPacket{ - SrcAddr: srcAddr, - DestAddr: destAddr, - Coins: coins, - SrcChain: srcChain, - DestChain: destChain, - } -} - -//nolint -func (p IBCPacket) GetSignBytes() []byte { - b, err := msgCdc.MarshalJSON(p) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) -} - -// validator the ibc packey -func (p IBCPacket) ValidateBasic() sdk.Error { - if p.SrcChain == p.DestChain { - return ErrIdenticalChains(DefaultCodespace).TraceSDK("") - } - if !p.Coins.IsValid() { - return sdk.ErrInvalidCoins("") - } - return nil -} - -// ---------------------------------- -// IBCTransferMsg - -// nolint - TODO rename to TransferMsg as folks will reference with ibc.TransferMsg -// IBCTransferMsg defines how another module can send an IBCPacket. -type IBCTransferMsg struct { - IBCPacket -} - -// nolint -func (msg IBCTransferMsg) Type() string { return "ibc" } - -// x/bank/tx.go MsgSend.GetSigners() -func (msg IBCTransferMsg) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.SrcAddr} } - -// get the sign bytes for ibc transfer message -func (msg IBCTransferMsg) GetSignBytes() []byte { - return msg.IBCPacket.GetSignBytes() -} - -// validate ibc transfer message -func (msg IBCTransferMsg) ValidateBasic() sdk.Error { - return msg.IBCPacket.ValidateBasic() -} - -// ---------------------------------- -// IBCReceiveMsg - -// nolint - TODO rename to ReceiveMsg as folks will reference with ibc.ReceiveMsg -// IBCReceiveMsg defines the message that a relayer uses to post an IBCPacket -// to the destination chain. -type IBCReceiveMsg struct { - IBCPacket - Relayer sdk.AccAddress - Sequence int64 -} - -// nolint -func (msg IBCReceiveMsg) Type() string { return "ibc" } -func (msg IBCReceiveMsg) ValidateBasic() sdk.Error { return msg.IBCPacket.ValidateBasic() } - -// x/bank/tx.go MsgSend.GetSigners() -func (msg IBCReceiveMsg) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Relayer} } - -// get the sign bytes for ibc receive message -func (msg IBCReceiveMsg) GetSignBytes() []byte { - b, err := msgCdc.MarshalJSON(struct { - IBCPacket json.RawMessage - Relayer sdk.AccAddress - Sequence int64 - }{ - IBCPacket: json.RawMessage(msg.IBCPacket.GetSignBytes()), - Relayer: msg.Relayer, - Sequence: msg.Sequence, - }) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) -} diff --git a/x/ibc/types_test.go b/x/ibc/types_test.go index e7c51d6b2477..c25b6a774286 100644 --- a/x/ibc/types_test.go +++ b/x/ibc/types_test.go @@ -1,5 +1,6 @@ package ibc +/* import ( "testing" @@ -108,4 +109,4 @@ func constructIBCPacket(valid bool) IBCPacket { return NewIBCPacket(srcAddr, destAddr, coins, srcChain, destChain) } return NewIBCPacket(srcAddr, destAddr, coins, srcChain, srcChain) -} +}*/ diff --git a/x/ibc/wire.go b/x/ibc/wire.go index f5644acc5350..480c5d927a1b 100644 --- a/x/ibc/wire.go +++ b/x/ibc/wire.go @@ -4,8 +4,13 @@ import ( "github.com/cosmos/cosmos-sdk/wire" ) -// Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - cdc.RegisterConcrete(IBCTransferMsg{}, "cosmos-sdk/IBCTransferMsg", nil) - cdc.RegisterConcrete(IBCReceiveMsg{}, "cosmos-sdk/IBCReceiveMsg", nil) + cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/ibc/Send", nil) + cdc.RegisterConcrete(MsgReceive{}, "cosmos-sdk/ibc/Receive", nil) + cdc.RegisterConcrete(MsgCleanup{}, "cosmos-sdk/ibc/Cleanup", nil) + cdc.RegisterConcrete(MsgOpenConnection{}, "cosmos-sdk/ibc/OpenConnection", nil) + cdc.RegisterConcrete(MsgUpdateConnection{}, "cosmos-sdk/ibc/UpdateConnection", nil) + + cdc.RegisterConcrete(Datagram{}, "cosmos-sdk/ibc/Datagram", nil) + cdc.RegisterInterface((*Payload)(nil), nil) }