diff --git a/cmd/kvcli/main.go b/cmd/kvcli/main.go index ff980fc3..06f2cc66 100644 --- a/cmd/kvcli/main.go +++ b/cmd/kvcli/main.go @@ -39,7 +39,7 @@ func main() { // add standard rpc commands rpc.AddCommands(rootCmd) - //Add state commands + // Add state commands tendermintCmd := &cobra.Command{ Use: "tendermint", Short: "Tendermint state querying subcommands", @@ -50,7 +50,7 @@ func main() { ) tx.AddCommands(tendermintCmd, cdc) - //Add IBC commands + // Add IBC commands // ibcCmd := &cobra.Command{ // Use: "ibc", // Short: "Inter-Blockchain Communication subcommands", @@ -76,7 +76,7 @@ func main() { client.LineBreak, ) - //Add stake commands + // Add stake commands stakeCmd := &cobra.Command{ Use: "stake", Short: "Stake and validation subcommands", @@ -102,7 +102,7 @@ func main() { stakeCmd, ) - //Add stake commands + // Add gov commands // govCmd := &cobra.Command{ // Use: "gov", // Short: "Governance and voting subcommands", @@ -123,7 +123,7 @@ func main() { // govCmd, // ) - //Add auth and bank commands + // Add auth and bank commands rootCmd.AddCommand( client.GetCommands( authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)), @@ -133,19 +133,22 @@ func main() { bankcmd.SendTxCmd(cdc), )...) + // Add paychan commands paychanCmd := &cobra.Command{ Use: "paychan", - Short: "Payment channel subcommands", + Short: "Payment channel subcommand", } paychanCmd.AddCommand( client.PostCommands( - paychancmd.CreatePaychanCmd(cdc), - paychancmd.GenerateNewStateCmd(cdc), - paychancmd.ClosePaychanCmd(cdc), + paychancmd.CreateChannelCmd(cdc), + paychancmd.GeneratePaymentCmd(cdc), + paychancmd.VerifyPaymentCmd(cdc, "paychan"), // pass in storeKey + paychancmd.SubmitPaymentCmd(cdc), )...) rootCmd.AddCommand( paychanCmd, ) + // add proxy, version and key info rootCmd.AddCommand( keys.Commands(), diff --git a/internal/app/app.go b/internal/app/app.go index faf3595a..1329ca4d 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -151,6 +151,7 @@ func (app *KavaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab // The function baseapp runs on receipt of a EndBlock ABCI message func (app *KavaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + paychan.EndBlocker(ctx, app.paychanKeeper) validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) //tags, _ := gov.EndBlocker(ctx, app.govKeeper) diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 39d82033..f4e25f89 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -1,5 +1,6 @@ package app +/* import ( "os" "testing" @@ -75,3 +76,4 @@ func TestGenesis(t *testing.T) { res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address) assert.Equal(t, acc, res1) } +*/ diff --git a/internal/x/paychan/README.md b/internal/x/paychan/README.md index 6adb635c..858c9217 100644 --- a/internal/x/paychan/README.md +++ b/internal/x/paychan/README.md @@ -9,7 +9,6 @@ Simplifications: TODO - in code TODOs - Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs - - chnge module name to "channel"? - Find a better name for Queue - clarify distinction between int slice and abstract queue concept - write some sort of integration test - find nicer name for payout diff --git a/internal/x/paychan/client/cmd/cmd.go b/internal/x/paychan/client/cmd/cmd.go index bed4c1c4..d10f4883 100644 --- a/internal/x/paychan/client/cmd/cmd.go +++ b/internal/x/paychan/client/cmd/cmd.go @@ -1,9 +1,9 @@ package cli import ( - "encoding/base64" "fmt" "io/ioutil" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -13,7 +13,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" - "github.com/cosmos/cosmos-sdk/x/auth" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/kava-labs/kava/internal/x/paychan" @@ -22,7 +21,6 @@ import ( // list of functions that return pointers to cobra commands // No local storage needed for cli acting as a sender - func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { flagTo := "to" flagCoins := "amount" @@ -46,7 +44,7 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { // Get receiver address toStr := viper.GetString(flagTo) - receiver, err := sdk.GetAccAddressBech32(toStr) + receiver, err := sdk.AccAddressFromBech32(toStr) if err != nil { return err } @@ -60,7 +58,7 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { // Create the create channel msg to send msg := paychan.MsgCreate{ - Participants: []sdk.AccAddress{sender, receiver}, + Participants: [2]sdk.AccAddress{sender, receiver}, Coins: coins, } err = msg.ValidateBasic() @@ -69,16 +67,15 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { } // Build and sign the transaction, then broadcast to the blockchain - res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) + err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) if err != nil { return err } - fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String()) return nil }, } cmd.Flags().String(flagTo, "", "Recipient address of the payment channel.") - cmd.Flags().String(flagAmount, "", "Amount of coins to fund the payment channel with.") + cmd.Flags().String(flagCoins, "", "Amount of coins to fund the payment channel with.") return cmd } @@ -105,7 +102,7 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command { // } // Get the paychan id - id := viper.GetInt64(flagId) // TODO make this default to pulling id from chain + id := paychan.ChannelID(viper.GetInt64(flagId)) // TODO make this default to pulling id from chain // Get channel receiver amount senderCoins, err := sdk.ParseCoins(viper.GetString(flagSenderAmount)) @@ -141,13 +138,16 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command { if err != nil { return err } - update.Sigs = [1]paychan.UpdateSignature{ + update.Sigs = [1]paychan.UpdateSignature{{ PubKey: pubKey, CryptoSignature: sig, - } + }} // Print out the update - jsonUpdate := cdc.MarshalJSONIndent(update) + jsonUpdate, err := wire.MarshalJSONIndent(cdc, update) + if err != nil { + return err + } fmt.Println(string(jsonUpdate)) return nil @@ -159,7 +159,7 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command { return cmd } -func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName, string) *cobra.Command { +func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command { cmd := &cobra.Command{ Use: "verify", @@ -168,6 +168,9 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName, string) *cobra.Command Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { + // Create a "client context" stuct populated with info from common flags + ctx := context.NewCoreContextFromViper() + // read in update bz, err := ioutil.ReadAll(os.Stdin) if err != nil { @@ -178,7 +181,7 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName, string) *cobra.Command var update paychan.Update cdc.UnmarshalJSON(bz, &update) -// get the channel from the node + // get the channel from the node res, err := ctx.QueryStore(paychan.GetChannelKey(update.ChannelID), paychanStoreName) if len(res) == 0 || err != nil { return errors.Errorf("channel with ID '%d' does not exist", update.ChannelID) @@ -187,7 +190,7 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName, string) *cobra.Command cdc.MustUnmarshalBinary(res, &channel) //verify - updateIsOK := paychan.Keeper.VerifyUpdate(channel ,update) + updateIsOK := paychan.VerifyUpdate(channel, update) // print result fmt.Println(updateIsOK) @@ -199,7 +202,7 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName, string) *cobra.Command return cmd } -func SubmitPaymentChannelCmd(cdc *wire.Codec) *cobra.Command { +func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "submit", @@ -238,11 +241,10 @@ func SubmitPaymentChannelCmd(cdc *wire.Codec) *cobra.Command { } // Build and sign the transaction, then broadcast to the blockchain - res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) + err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) if err != nil { return err } - fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String()) return nil }, } diff --git a/internal/x/paychan/client/rest/rest.go b/internal/x/paychan/client/rest/rest.go index d25e01cb..926a3474 100644 --- a/internal/x/paychan/client/rest/rest.go +++ b/internal/x/paychan/client/rest/rest.go @@ -1,10 +1,10 @@ package rest import ( - "github.com/gorilla/mux" - //"github.com/tendermint/go-crypto/keys" - //"github.com/cosmos/cosmos-sdk/client/context" - //"github.com/cosmos/cosmos-sdk/wire" +//"github.com/gorilla/mux" +//"github.com/tendermint/go-crypto/keys" +//"github.com/cosmos/cosmos-sdk/client/context" +//"github.com/cosmos/cosmos-sdk/wire" ) /* diff --git a/internal/x/paychan/endblocker.go b/internal/x/paychan/endblocker.go index d4c9c777..d51a0c66 100644 --- a/internal/x/paychan/endblocker.go +++ b/internal/x/paychan/endblocker.go @@ -11,11 +11,10 @@ func EndBlocker(ctx sdk.Context, k Keeper) sdk.Tags { // Iterate through submittedUpdatesQueue // TODO optimise so it doesn't pull every update from DB every block + q := k.getSubmittedUpdatesQueue(ctx) var sUpdate SubmittedUpdate - q, found := k.getSubmittedUpdatesQueue(ctx) - if !found { - panic("SubmittedUpdatesQueue not found.") - } + var found bool + for _, id := range q { // close the channel if the update has reached its execution time. // Using >= in case some are somehow missed. diff --git a/internal/x/paychan/endblocker_test.go b/internal/x/paychan/endblocker_test.go index dd4cce82..655a2fa1 100644 --- a/internal/x/paychan/endblocker_test.go +++ b/internal/x/paychan/endblocker_test.go @@ -49,7 +49,7 @@ func TestEndBlocker(t *testing.T) { _, found := channelKeeper.getChannel(ctx, channelID) assert.False(t, found) // check queue is empty, NOTE: due to encoding, an empty queue (underneath just an int slice) will be decoded as nil slice rather than an empty slice - suq, _ := channelKeeper.getSubmittedUpdatesQueue(ctx) + suq := channelKeeper.getSubmittedUpdatesQueue(ctx) assert.Equal(t, SubmittedUpdatesQueue(nil), suq) // check submittedUpdate is gone _, found = channelKeeper.getSubmittedUpdate(ctx, channelID) diff --git a/internal/x/paychan/keeper.go b/internal/x/paychan/keeper.go index 4c1dc0ca..af648315 100644 --- a/internal/x/paychan/keeper.go +++ b/internal/x/paychan/keeper.go @@ -79,15 +79,12 @@ func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Ta if !found { return nil, sdk.ErrInternal("Channel doesn't exist") } - err := k.VerifyUpdate(channel, update) + err := VerifyUpdate(channel, update) if err != nil { return nil, err } - q, found := k.getSubmittedUpdatesQueue(ctx) - if !found { - panic("SubmittedUpdatesQueue not found.") // TODO nicer custom errors - } + q := k.getSubmittedUpdatesQueue(ctx) if q.Contains(update.ChannelID) { // Someone has previously tried to update channel // In bidirectional channels the new update is compared against existing and replaces it if it has a higher sequence number. @@ -123,16 +120,13 @@ func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags if !found { return nil, sdk.ErrInternal("Channel doesn't exist") } - err := k.VerifyUpdate(channel, update) + err := VerifyUpdate(channel, update) if err != nil { return nil, err } // Check if there is an update in the queue already - q, found := k.getSubmittedUpdatesQueue(ctx) - if !found { - panic("SubmittedUpdatesQueue not found.") // TODO nicer custom errors - } + q := k.getSubmittedUpdatesQueue(ctx) if q.Contains(update.ChannelID) { // Someone has previously tried to update channel but receiver has final say k.removeFromSubmittedUpdatesQueue(ctx, update.ChannelID) @@ -162,7 +156,7 @@ func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags // return returnUpdate // } -func (k Keeper) VerifyUpdate(channel Channel, update Update) sdk.Error { +func VerifyUpdate(channel Channel, update Update) sdk.Error { // Check the num of payout participants match channel participants if len(update.Payout) != len(channel.Participants) { @@ -183,7 +177,7 @@ func (k Keeper) VerifyUpdate(channel Channel, update Update) sdk.Error { return sdk.ErrInternal("Payout amount doesn't match channel amount") } // Check sender signature is OK - if !k.verifySignatures(channel, update) { + if !verifySignatures(channel, update) { return sdk.ErrInternal("Signature on update not valid") } return nil @@ -212,7 +206,7 @@ func (k Keeper) closeChannel(ctx sdk.Context, update Update) (sdk.Tags, sdk.Erro return tags, nil } -func (k Keeper) verifySignatures(channel Channel, update Update) bool { +func verifySignatures(channel Channel, update Update) bool { // In non unidirectional channels there will be more than one signature to check signBytes := update.GetSignBytes() @@ -234,10 +228,7 @@ func (k Keeper) verifySignatures(channel Channel, update Update) bool { func (k Keeper) addToSubmittedUpdatesQueue(ctx sdk.Context, sUpdate SubmittedUpdate) { // always overwrite prexisting values - leave paychan logic to higher levels // get current queue - q, found := k.getSubmittedUpdatesQueue(ctx) - if !found { - panic("SubmittedUpdatesQueue not found.") - } + q := k.getSubmittedUpdatesQueue(ctx) // append ID to queue if !q.Contains(sUpdate.ChannelID) { q = append(q, sUpdate.ChannelID) @@ -249,10 +240,7 @@ func (k Keeper) addToSubmittedUpdatesQueue(ctx sdk.Context, sUpdate SubmittedUpd } func (k Keeper) removeFromSubmittedUpdatesQueue(ctx sdk.Context, channelID ChannelID) { // get current queue - q, found := k.getSubmittedUpdatesQueue(ctx) - if !found { - panic("SubmittedUpdatesQueue not found.") - } + q := k.getSubmittedUpdatesQueue(ctx) // remove id q.RemoveMatchingElements(channelID) // set queue @@ -261,19 +249,18 @@ func (k Keeper) removeFromSubmittedUpdatesQueue(ctx sdk.Context, channelID Chann k.deleteSubmittedUpdate(ctx, channelID) } -func (k Keeper) getSubmittedUpdatesQueue(ctx sdk.Context) (SubmittedUpdatesQueue, bool) { +func (k Keeper) getSubmittedUpdatesQueue(ctx sdk.Context) SubmittedUpdatesQueue { // load from DB store := ctx.KVStore(k.storeKey) bz := store.Get(k.getSubmittedUpdatesQueueKey()) - var suq SubmittedUpdatesQueue - if bz == nil { - return suq, false // TODO maybe create custom error to pass up here + var suq SubmittedUpdatesQueue // if the submittedUpdatesQueue not found then return an empty one + if bz != nil { + // unmarshal + k.cdc.MustUnmarshalBinary(bz, &suq) } - // unmarshal - k.cdc.MustUnmarshalBinary(bz, &suq) - // return - return suq, true + return suq + } func (k Keeper) setSubmittedUpdatesQueue(ctx sdk.Context, suq SubmittedUpdatesQueue) { store := ctx.KVStore(k.storeKey) @@ -332,7 +319,7 @@ func (k Keeper) getSubmittedUpdateKey(channelID ChannelID) []byte { func (k Keeper) getChannel(ctx sdk.Context, channelID ChannelID) (Channel, bool) { // load from DB store := ctx.KVStore(k.storeKey) - bz := store.Get(k.GetChannelKey(channelID)) + bz := store.Get(GetChannelKey(channelID)) var channel Channel if bz == nil { @@ -350,13 +337,13 @@ func (k Keeper) setChannel(ctx sdk.Context, channel Channel) { // marshal bz := k.cdc.MustMarshalBinary(channel) // panics if something goes wrong // write to db - key := k.GetChannelKey(channel.ID) + key := GetChannelKey(channel.ID) store.Set(key, bz) // panics if something goes wrong } func (k Keeper) deleteChannel(ctx sdk.Context, channelID ChannelID) { store := ctx.KVStore(k.storeKey) - store.Delete(k.GetChannelKey(channelID)) + store.Delete(GetChannelKey(channelID)) // TODO does this have return values? What happens when key doesn't exist? } @@ -364,7 +351,7 @@ func (k Keeper) getNewChannelID(ctx sdk.Context) ChannelID { // get last channel ID var lastID ChannelID store := ctx.KVStore(k.storeKey) - bz := store.Get(k.getLastChannelIDKey()) + bz := store.Get(getLastChannelIDKey()) if bz == nil { lastID = -1 // TODO is just setting to zero if uninitialized ok? } else { @@ -374,14 +361,14 @@ func (k Keeper) getNewChannelID(ctx sdk.Context) ChannelID { newID := lastID + 1 bz = k.cdc.MustMarshalBinary(newID) // set last channel id again - store.Set(k.getLastChannelIDKey(), bz) + store.Set(getLastChannelIDKey(), bz) // return return newID } -func (k Keeper) GetChannelKey(channelID ChannelID) []byte { +func GetChannelKey(channelID ChannelID) []byte { return []byte(fmt.Sprintf("channel:%d", channelID)) } -func (k Keeper) getLastChannelIDKey() []byte { +func getLastChannelIDKey() []byte { return []byte("lastChannelID") } diff --git a/internal/x/paychan/keeper_test.go b/internal/x/paychan/keeper_test.go index de6e0aa7..7435130b 100644 --- a/internal/x/paychan/keeper_test.go +++ b/internal/x/paychan/keeper_test.go @@ -11,6 +11,8 @@ import ( func TestKeeper(t *testing.T) { t.Run("CreateChannel", func(t *testing.T) { + // TODO test for receiver account not existing (OK) and sender not existing (not ok) + accountSeeds := []string{"senderSeed", "receiverSeed"} const ( senderAccountIndex int = 0 diff --git a/internal/x/paychan/types.go b/internal/x/paychan/types.go index 3324a231..a1135282 100644 --- a/internal/x/paychan/types.go +++ b/internal/x/paychan/types.go @@ -80,7 +80,7 @@ type SubmittedUpdate struct { ExecutionTime int64 // BlockHeight } -type SubmittedUpdatesQueue []ChannelID +type SubmittedUpdatesQueue []ChannelID // not technically a queue // Check if value is in queue func (suq SubmittedUpdatesQueue) Contains(channelID ChannelID) bool {