mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 08:15:19 +00:00
improve command UX
This commit is contained in:
parent
c168718332
commit
f58262c8b0
@ -141,6 +141,7 @@ func main() {
|
|||||||
paychanCmd.AddCommand(
|
paychanCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
paychancmd.CreateChannelCmd(cdc),
|
paychancmd.CreateChannelCmd(cdc),
|
||||||
|
paychancmd.GetChannelCmd(cdc, "paychan"), // pass in storeKey
|
||||||
paychancmd.GeneratePaymentCmd(cdc),
|
paychancmd.GeneratePaymentCmd(cdc),
|
||||||
paychancmd.VerifyPaymentCmd(cdc, "paychan"), // pass in storeKey
|
paychancmd.VerifyPaymentCmd(cdc, "paychan"), // pass in storeKey
|
||||||
paychancmd.SubmitPaymentCmd(cdc),
|
paychancmd.SubmitPaymentCmd(cdc),
|
||||||
|
@ -11,10 +11,14 @@ Simplifications:
|
|||||||
- Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs
|
- Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs
|
||||||
- Find a better name for Queue - clarify distinction between int slice and abstract queue concept
|
- Find a better name for Queue - clarify distinction between int slice and abstract queue concept
|
||||||
- write some sort of integration test
|
- write some sort of integration test
|
||||||
|
- possible bug in submitting same update repeatedly
|
||||||
- find nicer name for payout
|
- find nicer name for payout
|
||||||
- add Gas usage
|
- add Gas usage
|
||||||
- add tags (return channel id on creation)
|
- add tags (return channel id on creation)
|
||||||
- refactor cmds to be able to test them, then test them
|
- refactor cmds to be able to test them, then test them
|
||||||
|
- verify doesn’t throw json parsing error on invalid json
|
||||||
|
- can’t submit an update from an unitialised account
|
||||||
|
- pay without a --from returns confusing error
|
||||||
- use custom errors instead of using sdk.ErrInternal
|
- use custom errors instead of using sdk.ErrInternal
|
||||||
- split off signatures from update as with txs/msgs - testing easier, code easier to use, doesn't store sigs unecessarily on chain
|
- split off signatures from update as with txs/msgs - testing easier, code easier to use, doesn't store sigs unecessarily on chain
|
||||||
- consider removing pubKey from UpdateSignature - instead let channel module access accountMapper
|
- consider removing pubKey from UpdateSignature - instead let channel module access accountMapper
|
||||||
|
@ -3,7 +3,6 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -34,7 +33,8 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command {
|
|||||||
|
|
||||||
// Create a "client context" stuct populated with info from common flags
|
// Create a "client context" stuct populated with info from common flags
|
||||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||||
// ctx.PrintResponse = true TODO is this needed for channelID
|
// TODO is this needed for channelID
|
||||||
|
// ctx.PrintResponse = true
|
||||||
|
|
||||||
// Get sender adress
|
// Get sender adress
|
||||||
sender, err := ctx.GetFromAddress()
|
sender, err := ctx.GetFromAddress()
|
||||||
@ -80,26 +80,20 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command {
|
func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
flagId := "id" // ChannelID
|
flagId := "chan-id"
|
||||||
flagReceiverAmount := "r-amount" // amount the receiver should received on closing the channel
|
flagReceiverAmount := "rec-amt" // amount the receiver should received on closing the channel
|
||||||
flagSenderAmount := "s-amount" //
|
flagSenderAmount := "sen-amt"
|
||||||
|
flagPaymentFile := "filename"
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "pay",
|
Use: "pay",
|
||||||
Short: "Generate a .", // TODO descriptions
|
Short: "Generate a new payment.", // TODO descriptions
|
||||||
Long: "Generate a new ",
|
Long: "Generate a payment file (json) to send to the receiver as a payment.",
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// Create a "client context" stuct populated with info from common flags
|
// Create a "client context" stuct populated with info from common flags
|
||||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||||
// ctx.PrintResponse = false TODO is this needed to stop any other output messing up json?
|
|
||||||
|
|
||||||
// Get sender adress
|
|
||||||
// senderAddress, err := ctx.GetFromAddress()
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Get the paychan id
|
// Get the paychan id
|
||||||
id := paychan.ChannelID(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
|
||||||
@ -143,28 +137,35 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command {
|
|||||||
CryptoSignature: sig,
|
CryptoSignature: sig,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Print out the update
|
// Write out the update
|
||||||
jsonUpdate, err := wire.MarshalJSONIndent(cdc, update)
|
jsonUpdate, err := wire.MarshalJSONIndent(cdc, update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(string(jsonUpdate))
|
paymentFile := viper.GetString(flagPaymentFile)
|
||||||
|
err = ioutil.WriteFile(paymentFile, jsonUpdate, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Written payment out to %v.\n", paymentFile)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
|
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
|
||||||
cmd.Flags().String(flagSenderAmount, "", "")
|
cmd.Flags().String(flagSenderAmount, "", "Total coins to payout to sender on channel close.")
|
||||||
cmd.Flags().String(flagReceiverAmount, "", "")
|
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.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
|
func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
|
||||||
|
flagPaymentFile := "payment"
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "verify",
|
Use: "verify",
|
||||||
Short: "", // TODO
|
Short: "Verify a payment file.",
|
||||||
Long: "",
|
Long: "Verify that a received payment can be used to close a channel.",
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
@ -172,7 +173,7 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
|
|||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// read in update
|
// read in update
|
||||||
bz, err := ioutil.ReadAll(os.Stdin)
|
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO add nice message about how to feed in stdin
|
// TODO add nice message about how to feed in stdin
|
||||||
return err
|
return err
|
||||||
@ -190,24 +191,30 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
|
|||||||
cdc.MustUnmarshalBinary(res, &channel)
|
cdc.MustUnmarshalBinary(res, &channel)
|
||||||
|
|
||||||
//verify
|
//verify
|
||||||
updateIsOK := paychan.VerifyUpdate(channel, update)
|
verificationError := paychan.VerifyUpdate(channel, update)
|
||||||
|
|
||||||
// print result
|
// print result
|
||||||
fmt.Println(updateIsOK)
|
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)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
cmd.Flags().String(flagPaymentFile, "payment.json", "File name to read the payment from.")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
|
func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
|
flagPaymentFile := "payment"
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "submit",
|
Use: "submit",
|
||||||
Short: "",
|
Short: "Submit a payment to the blockchain to close the channel.",
|
||||||
Long: "",
|
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),
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
@ -222,7 +229,7 @@ func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read in update
|
// read in update
|
||||||
bz, err := ioutil.ReadAll(os.Stdin)
|
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -248,152 +255,44 @@ func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
cmd.Flags().String(flagPaymentFile, "payment.json", "File to read the payment from.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func GetChannelCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
|
||||||
func ClosePaychanCmd(cdc *wire.Codec) *cobra.Command {
|
flagId := "chan-id"
|
||||||
flagState := "state"
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "close",
|
Use: "get",
|
||||||
Short: "Close a payment channel, given a state",
|
Short: "Get info on a channel.",
|
||||||
Long: "Close an existing payment channel with a state received from a sender. This signs it as the receiver before submitting to the blockchain.",
|
Long: "Get information on a non closed channel.",
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
|
||||||
|
|
||||||
// Get the sender-signed close tx
|
// Create a "client context" stuct populated with info from common flags
|
||||||
state := viper.GetString(flagState)
|
ctx := context.NewCoreContextFromViper()
|
||||||
txBytes, err := base64.StdEncoding.DecodeString(state)
|
|
||||||
if err != nil {
|
// Get channel ID
|
||||||
return err
|
id := paychan.ChannelID(viper.GetInt64(flagId))
|
||||||
|
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
stdTx := auth.StdTx{}
|
var channel paychan.Channel
|
||||||
cdc.UnmarshalBinary(txBytes, &stdTx)
|
cdc.MustUnmarshalBinary(res, &channel)
|
||||||
|
|
||||||
// Sign close tx
|
// Convert the channel to a json object for pretty printing
|
||||||
|
jsonChannel, err := wire.MarshalJSONIndent(cdc, channel)
|
||||||
// ensure contxt has up to date account and sequence numbers
|
|
||||||
ctx, err = Ensure(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Sign message (asks user for password)
|
|
||||||
_, sig, err := UserSignMsg(ctx, ctx.FromAddressName, stdTx.Msg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append signature to close tx
|
// print out json channel
|
||||||
stdTx.Signatures = append(stdTx.Signatures, sig)
|
fmt.Println(string(jsonChannel))
|
||||||
// encode close tx
|
|
||||||
txBytes, err = cdc.MarshalBinary(stdTx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Broadcast close tx to the blockchain
|
|
||||||
|
|
||||||
res, err := ctx.BroadcastTx(txBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().String(flagState, "", "State received from sender.")
|
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPER FUNCTIONS
|
|
||||||
// This is a partial refactor of cosmos-sdk/client/context.
|
|
||||||
// Existing API was awkard to use for paychans.
|
|
||||||
|
|
||||||
func Ensure(ctx context.CoreContext) (context.CoreContext, error) {
|
|
||||||
|
|
||||||
ctx, err := context.EnsureAccountNumber(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
|
||||||
// default to next sequence number if none provided
|
|
||||||
ctx, err = context.EnsureSequence(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
|
||||||
return ctx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UserSignMsg(ctx context.CoreContext, name string, msg sdk.Msg) (signMsg auth.StdSignMsg, stdSig auth.StdSignature, err error) {
|
|
||||||
|
|
||||||
// TODO check how to handle non error return values on error. Returning empty versions doesn't seem right.
|
|
||||||
|
|
||||||
passphrase, err := ctx.GetPassphraseFromStdin(name)
|
|
||||||
if err != nil {
|
|
||||||
return signMsg, stdSig, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// build the Sign Messsage from the Standard Message
|
|
||||||
chainID := ctx.ChainID
|
|
||||||
if chainID == "" {
|
|
||||||
return signMsg, stdSig, errors.Errorf("Chain ID required but not specified")
|
|
||||||
}
|
|
||||||
accnum := ctx.AccountNumber
|
|
||||||
sequence := ctx.Sequence
|
|
||||||
|
|
||||||
signMsg = auth.StdSignMsg{
|
|
||||||
ChainID: chainID,
|
|
||||||
AccountNumbers: []int64{accnum},
|
|
||||||
Sequences: []int64{sequence},
|
|
||||||
Msg: msg,
|
|
||||||
Fee: auth.NewStdFee(ctx.Gas, sdk.Coin{}), // TODO run simulate to estimate gas?
|
|
||||||
}
|
|
||||||
|
|
||||||
keybase, err := keys.GetKeyBase()
|
|
||||||
if err != nil {
|
|
||||||
return signMsg, stdSig, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// sign and build
|
|
||||||
bz := signMsg.Bytes()
|
|
||||||
|
|
||||||
sig, pubkey, err := keybase.Sign(name, passphrase, bz)
|
|
||||||
if err != nil {
|
|
||||||
return signMsg, stdSig, err
|
|
||||||
}
|
|
||||||
stdSig = auth.StdSignature{
|
|
||||||
PubKey: pubkey,
|
|
||||||
Signature: sig,
|
|
||||||
AccountNumber: accnum,
|
|
||||||
Sequence: sequence,
|
|
||||||
}
|
|
||||||
|
|
||||||
return signMsg, stdSig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Build(cdc *wire.Codec, signMsg auth.StdSignMsg, sig auth.StdSignature) ([]byte, error) {
|
|
||||||
tx := auth.NewStdTx(signMsg.Msg, signMsg.Fee, []auth.StdSignature{sig})
|
|
||||||
return cdc.MarshalBinary(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnsureSignBuild(ctx context.CoreContext, name string, msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
|
|
||||||
//Ensure context has up to date account and sequence numbers
|
|
||||||
ctx, err := Ensure(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Sign message (asks user for password)
|
|
||||||
signMsg, sig, err := UserSignMsg(ctx, name, msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Create tx and marshal
|
|
||||||
txBytes, err := Build(cdc, signMsg, sig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return txBytes, nil
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -9,12 +9,10 @@ func RegisterWire(cdc *wire.Codec) {
|
|||||||
cdc.RegisterConcrete(MsgSubmitUpdate{}, "paychan/MsgSubmitUpdate", nil)
|
cdc.RegisterConcrete(MsgSubmitUpdate{}, "paychan/MsgSubmitUpdate", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move this to near the msg definitions
|
||||||
var msgCdc = wire.NewCodec()
|
var msgCdc = wire.NewCodec()
|
||||||
|
|
||||||
/*
|
|
||||||
func init() {
|
func init() {
|
||||||
|
wire.RegisterCrypto(msgCdc)
|
||||||
RegisterWire(msgCdc)
|
RegisterWire(msgCdc)
|
||||||
// TODO is this needed?
|
|
||||||
//wire.RegisterCrypto(msgCdc)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
Loading…
Reference in New Issue
Block a user