0g-chain/internal/x/paychan/client/cli/cmd.go

317 lines
9.3 KiB
Go
Raw Normal View History

2018-07-08 22:09:07 +00:00
package cli
import (
"fmt"
2018-09-01 20:41:40 +00:00
"io/ioutil"
2018-07-08 22:09:07 +00:00
2018-07-14 22:27:56 +00:00
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
2018-07-14 00:09:02 +00:00
"github.com/cosmos/cosmos-sdk/client/context"
2018-07-14 22:27:56 +00:00
"github.com/cosmos/cosmos-sdk/client/keys"
2018-07-14 00:09:02 +00:00
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
2018-07-15 14:08:08 +00:00
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
2018-07-14 22:27:56 +00:00
2018-07-14 00:09:02 +00:00
"github.com/kava-labs/kava/internal/x/paychan"
2018-07-08 22:09:07 +00:00
)
// list of functions that return pointers to cobra commands
// No local storage needed for cli acting as a sender
2018-09-01 20:41:40 +00:00
func CreateChannelCmd(cdc *wire.Codec) *cobra.Command {
2018-07-14 00:09:02 +00:00
flagTo := "to"
2018-09-01 20:41:40 +00:00
flagCoins := "amount"
2018-07-14 00:09:02 +00:00
cmd := &cobra.Command{
Use: "create",
Short: "Create a new payment channel",
2018-09-01 20:41:40 +00:00
Long: "Create a new unidirectional payment channel from a local address to a remote address, funded with some amount of coins. These coins are removed from the sender account and put into the channel.",
2018-07-14 00:09:02 +00:00
Args: cobra.NoArgs,
2018-07-08 22:09:07 +00:00
RunE: func(cmd *cobra.Command, args []string) error {
2018-07-14 22:27:56 +00:00
// Create a "client context" stuct populated with info from common flags
2018-07-15 14:08:08 +00:00
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
2018-09-02 02:22:50 +00:00
// TODO is this needed for channelID
// ctx.PrintResponse = true
2018-07-08 22:09:07 +00:00
2018-07-14 00:09:02 +00:00
// Get sender adress
2018-09-01 20:41:40 +00:00
sender, err := ctx.GetFromAddress()
2018-07-14 00:09:02 +00:00
if err != nil {
return err
}
2018-07-08 22:09:07 +00:00
2018-07-14 00:09:02 +00:00
// Get receiver address
toStr := viper.GetString(flagTo)
2018-09-01 23:29:51 +00:00
receiver, err := sdk.AccAddressFromBech32(toStr)
2018-07-08 22:09:07 +00:00
if err != nil {
return err
}
2018-07-14 00:09:02 +00:00
// Get channel funding amount
2018-09-01 20:41:40 +00:00
coinsString := viper.GetString(flagCoins)
coins, err := sdk.ParseCoins(coinsString)
2018-07-08 22:09:07 +00:00
if err != nil {
return err
}
2018-07-14 00:09:02 +00:00
// Create the create channel msg to send
msg := paychan.MsgCreate{
2018-09-01 23:29:51 +00:00
Participants: [2]sdk.AccAddress{sender, receiver},
2018-09-01 20:41:40 +00:00
Coins: coins,
}
err = msg.ValidateBasic()
if err != nil {
return err
2018-07-08 22:09:07 +00:00
}
2018-09-01 20:41:40 +00:00
2018-07-14 22:27:56 +00:00
// Build and sign the transaction, then broadcast to the blockchain
2018-09-01 23:29:51 +00:00
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
2018-07-14 00:09:02 +00:00
if err != nil {
return err
}
2018-07-14 22:27:56 +00:00
return nil
2018-07-14 00:09:02 +00:00
},
}
2018-09-01 20:41:40 +00:00
cmd.Flags().String(flagTo, "", "Recipient address of the payment channel.")
2018-09-01 23:29:51 +00:00
cmd.Flags().String(flagCoins, "", "Amount of coins to fund the payment channel with.")
2018-07-14 00:09:02 +00:00
return cmd
}
2018-07-08 22:09:07 +00:00
2018-09-01 20:41:40 +00:00
func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command {
2018-09-02 02:22:50 +00:00
flagId := "chan-id"
flagReceiverAmount := "rec-amt" // amount the receiver should received on closing the channel
flagSenderAmount := "sen-amt"
flagPaymentFile := "filename"
2018-07-14 00:09:02 +00:00
cmd := &cobra.Command{
2018-09-01 20:41:40 +00:00
Use: "pay",
2018-09-02 02:22:50 +00:00
Short: "Generate a new payment.", // TODO descriptions
Long: "Generate a payment file (json) to send to the receiver as a payment.",
2018-07-14 00:09:02 +00:00
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
2018-07-14 22:27:56 +00:00
// Create a "client context" stuct populated with info from common flags
2018-07-15 14:08:08 +00:00
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
2018-07-14 22:27:56 +00:00
// Get the paychan id
2018-09-01 23:29:51 +00:00
id := paychan.ChannelID(viper.GetInt64(flagId)) // TODO make this default to pulling id from chain
2018-07-14 22:27:56 +00:00
2018-09-01 20:41:40 +00:00
// Get channel receiver amount
senderCoins, err := sdk.ParseCoins(viper.GetString(flagSenderAmount))
2018-07-14 00:09:02 +00:00
if err != nil {
return err
}
// Get channel receiver amount
2018-09-01 20:41:40 +00:00
receiverCoins, err := sdk.ParseCoins(viper.GetString(flagReceiverAmount))
2018-07-14 00:09:02 +00:00
if err != nil {
return err
}
2018-07-14 22:27:56 +00:00
// create close paychan msg
2018-09-01 20:41:40 +00:00
update := paychan.Update{
ChannelID: id,
Payout: paychan.Payout{senderCoins, receiverCoins},
// empty sigs
}
// Sign the update as the sender
keybase, err := keys.GetKeyBase()
if err != nil {
return err
}
name := ctx.FromAddressName
passphrase, err := ctx.GetPassphraseFromStdin(name)
if err != nil {
return err
2018-07-14 00:09:02 +00:00
}
2018-09-01 20:41:40 +00:00
bz := update.GetSignBytes()
2018-07-14 00:09:02 +00:00
2018-09-01 20:41:40 +00:00
sig, pubKey, err := keybase.Sign(name, passphrase, bz)
2018-07-08 22:09:07 +00:00
if err != nil {
return err
}
2018-09-01 23:29:51 +00:00
update.Sigs = [1]paychan.UpdateSignature{{
2018-09-01 20:41:40 +00:00
PubKey: pubKey,
CryptoSignature: sig,
2018-09-01 23:29:51 +00:00
}}
2018-09-01 20:41:40 +00:00
2018-09-02 02:22:50 +00:00
// Write out the update
2018-09-01 23:29:51 +00:00
jsonUpdate, err := wire.MarshalJSONIndent(cdc, update)
if err != nil {
return err
}
2018-09-02 02:22:50 +00:00
paymentFile := viper.GetString(flagPaymentFile)
err = ioutil.WriteFile(paymentFile, jsonUpdate, 0644)
if err != nil {
return err
}
fmt.Printf("Written payment out to %v.\n", paymentFile)
2018-07-14 00:09:02 +00:00
2018-07-14 22:27:56 +00:00
return nil
2018-07-14 00:09:02 +00:00
},
}
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
2018-09-02 02:22:50 +00:00
cmd.Flags().String(flagSenderAmount, "", "Total coins to payout to sender on channel close.")
cmd.Flags().String(flagReceiverAmount, "", "Total coins to payout to sender on channel close.")
cmd.Flags().String(flagPaymentFile, "payment.json", "File name to write the payment into.")
2018-07-14 00:09:02 +00:00
return cmd
}
2018-09-01 23:29:51 +00:00
func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
2018-09-02 02:22:50 +00:00
flagPaymentFile := "payment"
2018-09-01 20:41:40 +00:00
cmd := &cobra.Command{
Use: "verify",
2018-09-02 02:22:50 +00:00
Short: "Verify a payment file.",
Long: "Verify that a received payment can be used to close a channel.",
2018-09-01 20:41:40 +00:00
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
2018-09-01 23:29:51 +00:00
// Create a "client context" stuct populated with info from common flags
ctx := context.NewCoreContextFromViper()
2018-09-01 20:41:40 +00:00
// read in update
2018-09-02 02:22:50 +00:00
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
2018-09-01 20:41:40 +00:00
if err != nil {
// TODO add nice message about how to feed in stdin
return err
}
// decode json
var update paychan.Update
cdc.UnmarshalJSON(bz, &update)
2018-09-01 23:29:51 +00:00
// get the channel from the node
2018-09-01 20:41:40 +00:00
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)
}
var channel paychan.Channel
cdc.MustUnmarshalBinary(res, &channel)
//verify
2018-09-02 02:22:50 +00:00
verificationError := paychan.VerifyUpdate(channel, update)
2018-09-01 20:41:40 +00:00
// print result
2018-09-02 02:22:50 +00:00
if verificationError == nil {
fmt.Printf("Payment is valid for channel '%d'.\n", update.ChannelID)
} else {
fmt.Printf("Payment is NOT valid for channel '%d'.\n", update.ChannelID)
fmt.Println(verificationError)
}
2018-09-01 20:41:40 +00:00
return nil
},
}
2018-09-02 02:22:50 +00:00
cmd.Flags().String(flagPaymentFile, "payment.json", "File name to read the payment from.")
2018-09-01 20:41:40 +00:00
return cmd
}
2018-09-01 23:29:51 +00:00
func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
2018-09-02 02:22:50 +00:00
flagPaymentFile := "payment"
2018-09-01 20:41:40 +00:00
cmd := &cobra.Command{
Use: "submit",
2018-09-02 02:22:50 +00:00
Short: "Submit a payment to the blockchain to close the channel.",
Long: fmt.Sprintf("Submit a payment to the blockchain to either close a channel immediately (if you are the receiver) or after a dispute period of %d blocks (if you are the sender).", paychan.ChannelDisputeTime),
2018-09-01 20:41:40 +00:00
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().WithDecoder(authcmd.GetAccountDecoder(cdc))
// ctx.PrintResponse = true TODO is this needed for channelID
// Get sender adress
submitter, err := ctx.GetFromAddress()
if err != nil {
return err
}
// read in update
2018-09-02 02:22:50 +00:00
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
2018-09-01 20:41:40 +00:00
if err != nil {
return err
}
// decode json
var update paychan.Update
cdc.UnmarshalJSON(bz, &update)
// Create the create channel msg to send
msg := paychan.MsgSubmitUpdate{
Update: update,
Submitter: submitter,
}
err = msg.ValidateBasic()
if err != nil {
return err
}
// Build and sign the transaction, then broadcast to the blockchain
2018-09-01 23:29:51 +00:00
err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
2018-09-01 20:41:40 +00:00
if err != nil {
return err
}
return nil
},
}
2018-09-02 02:22:50 +00:00
cmd.Flags().String(flagPaymentFile, "payment.json", "File to read the payment from.")
2018-09-01 20:41:40 +00:00
return cmd
}
2018-09-02 02:22:50 +00:00
func GetChannelCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
flagId := "chan-id"
2018-07-14 22:27:56 +00:00
cmd := &cobra.Command{
2018-09-02 02:22:50 +00:00
Use: "get",
Short: "Get info on a channel.",
Long: "Get the details of a non closed channel plus any submitted update waiting to be executed.",
2018-07-14 22:27:56 +00:00
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
2018-09-02 02:22:50 +00:00
// Create a "client context" stuct populated with info from common flags
ctx := context.NewCoreContextFromViper()
2018-07-14 22:27:56 +00:00
2018-09-02 02:22:50 +00:00
// Get channel ID
id := paychan.ChannelID(viper.GetInt64(flagId))
2018-07-14 22:27:56 +00:00
2018-09-02 02:22:50 +00:00
// Get the channel from the node
res, err := ctx.QueryStore(paychan.GetChannelKey(id), paychanStoreName)
if len(res) == 0 || err != nil {
return errors.Errorf("channel with ID '%d' does not exist", id)
2018-07-14 22:27:56 +00:00
}
2018-09-02 02:22:50 +00:00
var channel paychan.Channel
cdc.MustUnmarshalBinary(res, &channel)
2018-07-14 22:27:56 +00:00
2018-09-02 02:22:50 +00:00
// Convert the channel to a json object for pretty printing
jsonChannel, err := wire.MarshalJSONIndent(cdc, channel)
2018-07-14 22:27:56 +00:00
if err != nil {
return err
}
2018-09-02 02:22:50 +00:00
// print out json channel
fmt.Println(string(jsonChannel))
// Get any submitted updates from the node
res, err = ctx.QueryStore(paychan.GetSubmittedUpdateKey(id), paychanStoreName)
if err != nil {
return err
}
// Print out the submited update if it exsits
if len(res) != 0 {
var submittedUpdate paychan.SubmittedUpdate
cdc.MustUnmarshalBinary(res, &submittedUpdate)
// Convert the submitted update to a json object for pretty printing
jsonSU, err := wire.MarshalJSONIndent(cdc, submittedUpdate)
if err != nil {
return err
}
// print out json submitted update
fmt.Println(string(jsonSU))
}
2018-07-14 22:27:56 +00:00
return nil
},
}
2018-09-02 02:22:50 +00:00
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
2018-07-14 22:27:56 +00:00
return cmd
}