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." ,
2018-09-03 20:38:51 +00:00
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 ) )
2018-09-03 20:38:51 +00:00
// 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
}