From 7b0934bf9fcf865f9f87ab28b751858183772365 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 18 Jul 2017 20:12:51 +0200 Subject: [PATCH] Ripped about cmd logic to make middleware modular --- cmd/basecli/commands/cmds.go | 216 +++++------------- cmd/basecli/commands/query.go | 68 +----- cmd/basecli/main.go | 34 ++- .../cmd/countercli/commands/counter.go | 29 +-- docs/guide/counter/cmd/countercli/main.go | 19 +- modules/auth/commands/wrap.go | 35 +++ modules/base/commands/wrap.go | 40 ++++ modules/coin/commands/query.go | 39 ++++ modules/coin/commands/tx.go | 89 ++++++++ modules/fee/commands/wrap.go | 42 ++++ modules/nonce/commands/query.go | 46 ++++ modules/nonce/commands/wrap.go | 41 ++++ 12 files changed, 435 insertions(+), 263 deletions(-) create mode 100644 modules/auth/commands/wrap.go create mode 100644 modules/base/commands/wrap.go create mode 100644 modules/coin/commands/query.go create mode 100644 modules/coin/commands/tx.go create mode 100644 modules/fee/commands/wrap.go create mode 100644 modules/nonce/commands/query.go create mode 100644 modules/nonce/commands/wrap.go diff --git a/cmd/basecli/commands/cmds.go b/cmd/basecli/commands/cmds.go index 3732f38fc0..e1b979ee35 100644 --- a/cmd/basecli/commands/cmds.go +++ b/cmd/basecli/commands/cmds.go @@ -6,10 +6,8 @@ import ( "strings" "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/spf13/pflag" - "github.com/tendermint/light-client/commands" txcmd "github.com/tendermint/light-client/commands/txs" cmn "github.com/tendermint/tmlibs/common" @@ -17,85 +15,44 @@ import ( "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/modules/auth" - "github.com/tendermint/basecoin/modules/base" - "github.com/tendermint/basecoin/modules/coin" - "github.com/tendermint/basecoin/modules/fee" - "github.com/tendermint/basecoin/modules/nonce" ) -//------------------------- -// SendTx - -// SendTxCmd is CLI command to send tokens between basecoin accounts -var SendTxCmd = &cobra.Command{ - Use: "send", - Short: "send tokens from one account to another", - RunE: commands.RequireInit(doSendTx), -} - -//nolint -const ( - FlagTo = "to" - FlagAmount = "amount" - FlagFee = "fee" - FlagGas = "gas" - FlagExpires = "expires" - FlagSequence = "sequence" +var ( + // Middleware must be set in main.go to defined the wrappers we should apply + Middleware Wrapper ) -func init() { - flags := SendTxCmd.Flags() - flags.String(FlagTo, "", "Destination address for the bits") - flags.String(FlagAmount, "", "Coins to send in the format ,...") - flags.String(FlagFee, "0mycoin", "Coins for the transaction fee of the format ") - flags.Uint64(FlagGas, 0, "Amount of gas for this transaction") - flags.Uint64(FlagExpires, 0, "Block height at which this tx expires") - flags.Int(FlagSequence, -1, "Sequence number for this transaction") +// Wrapper defines the information needed for each middleware package that +// wraps the data. They should read all configuration out of bounds via viper. +type Wrapper interface { + Wrap(basecoin.Tx) (basecoin.Tx, error) + Register(*pflag.FlagSet) } -// doSendTx is an example of how to make a tx -func doSendTx(cmd *cobra.Command, args []string) error { - // load data from json or flags - var tx basecoin.Tx - found, err := txcmd.LoadJSON(&tx) - if err != nil { - return err - } - if !found { - tx, err = readSendTxFlags() - } - if err != nil { - return err - } +// Wrappers combines a list of wrapper middlewares. +// The first one is the inner-most layer, eg. Fee, Nonce, Chain, Auth +type Wrappers []Wrapper - // TODO: make this more flexible for middleware - tx, err = WrapFeeTx(tx) - if err != nil { - return err - } - tx, err = WrapNonceTx(tx) - if err != nil { - return err - } - tx, err = WrapChainTx(tx) - if err != nil { - return err - } +var _ Wrapper = Wrappers{} - // Note: this is single sig (no multi sig yet) - stx := auth.NewSig(tx) - - // Sign if needed and post. This it the work-horse - bres, err := txcmd.SignAndPostTx(stx) - if err != nil { - return err - } - if err = ValidateResult(bres); err != nil { - return err +// Wrap applies the wrappers to the passed in tx in order, +// aborting on the first error +func (ws Wrappers) Wrap(tx basecoin.Tx) (basecoin.Tx, error) { + var err error + for _, w := range ws { + tx, err = w.Wrap(tx) + if err != nil { + break + } } + return tx, err +} - // Output result - return txcmd.OutputTx(bres) +// Register adds any needed flags to the command +func (ws Wrappers) Register(fs *pflag.FlagSet) { + for _, w := range ws { + w.Register(fs) + } } // ValidateResult returns an appropriate error if the server rejected the @@ -110,44 +67,34 @@ func ValidateResult(res *ctypes.ResultBroadcastTxCommit) error { return nil } -// WrapNonceTx grabs the sequence number from the flag and wraps -// the tx with this nonce. Grabs the permission from the signer, -// as we still only support single sig on the cli -func WrapNonceTx(tx basecoin.Tx) (res basecoin.Tx, err error) { - //add the nonce tx layer to the tx - seq := viper.GetInt(FlagSequence) - if seq < 0 { - return res, fmt.Errorf("sequence must be greater than 0") - } - signers := []basecoin.Actor{GetSignerAct()} - res = nonce.NewTx(uint32(seq), signers, tx) - return -} +// ParseAddress parses an address of form: +// [:][:] +// into a basecoin.Actor. +// If app is not specified or "", then assume auth.NameSigs +func ParseAddress(input string) (res basecoin.Actor, err error) { + chain, app := "", auth.NameSigs + spl := strings.SplitN(input, ":", 3) -// WrapFeeTx checks for FlagFee and if present wraps the tx with a -// FeeTx of the given amount, paid by the signer -func WrapFeeTx(tx basecoin.Tx) (res basecoin.Tx, err error) { - //parse the fee and amounts into coin types - toll, err := coin.ParseCoin(viper.GetString(FlagFee)) + if len(spl) == 3 { + chain = spl[0] + spl = spl[1:] + } + if len(spl) == 2 { + if spl[0] != "" { + app = spl[0] + } + spl = spl[1:] + } + + addr, err := hex.DecodeString(cmn.StripHex(spl[0])) if err != nil { - return res, err + return res, errors.Errorf("Address is invalid hex: %v\n", err) } - // if no fee, do nothing, otherwise wrap it - if toll.IsZero() { - return tx, nil + res = basecoin.Actor{ + ChainID: chain, + App: app, + Address: addr, } - res = fee.NewFee(tx, toll, GetSignerAct()) - return -} - -// WrapChainTx will wrap the tx with a ChainTx from the standard flags -func WrapChainTx(tx basecoin.Tx) (res basecoin.Tx, err error) { - expires := viper.GetInt64(FlagExpires) - chain := commands.GetChainID() - if chain == "" { - return res, errors.New("No chain-id provided") - } - res = base.NewChainTx(chain, uint64(expires), tx) return } @@ -161,60 +108,3 @@ func GetSignerAct() (res basecoin.Actor) { } return res } - -func readSendTxFlags() (tx basecoin.Tx, err error) { - // parse to address - chain, to, err := parseChainAddress(viper.GetString(FlagTo)) - if err != nil { - return tx, err - } - toAddr := auth.SigPerm(to) - toAddr.ChainID = chain - - amountCoins, err := coin.ParseCoins(viper.GetString(FlagAmount)) - if err != nil { - return tx, err - } - - // craft the inputs and outputs - ins := []coin.TxInput{{ - Address: GetSignerAct(), - Coins: amountCoins, - }} - outs := []coin.TxOutput{{ - Address: toAddr, - Coins: amountCoins, - }} - - return coin.NewSendTx(ins, outs), nil -} - -func parseChainAddress(toFlag string) (string, []byte, error) { - var toHex string - var chainPrefix string - spl := strings.Split(toFlag, "/") - switch len(spl) { - case 1: - toHex = spl[0] - case 2: - chainPrefix = spl[0] - toHex = spl[1] - default: - return "", nil, errors.Errorf("To address has too many slashes") - } - - // convert destination address to bytes - to, err := hex.DecodeString(cmn.StripHex(toHex)) - if err != nil { - return "", nil, errors.Errorf("To address is invalid hex: %v\n", err) - } - - return chainPrefix, to, nil -} - -/** TODO copied from basecoin cli - put in common somewhere? **/ - -// ParseHexFlag parses a flag string to byte array -func ParseHexFlag(flag string) ([]byte, error) { - return hex.DecodeString(cmn.StripHex(viper.GetString(flag))) -} diff --git a/cmd/basecli/commands/query.go b/cmd/basecli/commands/query.go index ce3fa338ca..2fd85a6c73 100644 --- a/cmd/basecli/commands/query.go +++ b/cmd/basecli/commands/query.go @@ -1,78 +1,12 @@ package commands import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/tendermint/basecoin" wire "github.com/tendermint/go-wire" - lc "github.com/tendermint/light-client" - lcmd "github.com/tendermint/light-client/commands" - proofcmd "github.com/tendermint/light-client/commands/proofs" "github.com/tendermint/light-client/proofs" - "github.com/tendermint/basecoin/modules/auth" - "github.com/tendermint/basecoin/modules/coin" - "github.com/tendermint/basecoin/modules/nonce" - "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin" ) -// AccountQueryCmd - command to query an account -var AccountQueryCmd = &cobra.Command{ - Use: "account [address]", - Short: "Get details of an account, with proof", - RunE: lcmd.RequireInit(doAccountQuery), -} - -func doAccountQuery(cmd *cobra.Command, args []string) error { - addr, err := proofcmd.ParseHexKey(args, "address") - if err != nil { - return err - } - key := stack.PrefixedKey(coin.NameCoin, auth.SigPerm(addr).Bytes()) - - acc := coin.Account{} - proof, err := proofcmd.GetAndParseAppProof(key, &acc) - if lc.IsNoDataErr(err) { - return errors.Errorf("Account bytes are empty for address %X ", addr) - } else if err != nil { - return err - } - - return proofcmd.OutputProof(acc, proof.BlockHeight()) -} - -// NonceQueryCmd - command to query an nonce account -var NonceQueryCmd = &cobra.Command{ - Use: "nonce [address]", - Short: "Get details of a nonce sequence number, with proof", - RunE: lcmd.RequireInit(doNonceQuery), -} - -func doNonceQuery(cmd *cobra.Command, args []string) error { - addr, err := proofcmd.ParseHexKey(args, "address") - if err != nil { - return err - } - - act := []basecoin.Actor{basecoin.NewActor( - auth.NameSigs, - addr, - )} - - key := stack.PrefixedKey(nonce.NameNonce, nonce.GetSeqKey(act)) - - var seq uint32 - proof, err := proofcmd.GetAndParseAppProof(key, &seq) - if lc.IsNoDataErr(err) { - return errors.Errorf("Sequence is empty for address %X ", addr) - } else if err != nil { - return err - } - - return proofcmd.OutputProof(seq, proof.BlockHeight()) -} - // BaseTxPresenter this decodes all basecoin tx type BaseTxPresenter struct { proofs.RawPresenter // this handles MakeKey as hex bytes diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go index 48dcf1dd59..be87d2d583 100644 --- a/cmd/basecli/main.go +++ b/cmd/basecli/main.go @@ -1,10 +1,12 @@ package main import ( + "fmt" "os" "github.com/spf13/cobra" + "github.com/tendermint/abci/version" keycmd "github.com/tendermint/go-crypto/cmd" "github.com/tendermint/light-client/commands" "github.com/tendermint/light-client/commands/proofs" @@ -15,7 +17,11 @@ import ( "github.com/tendermint/tmlibs/cli" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" - coincmd "github.com/tendermint/basecoin/cmd/basecoin/commands" + authcmd "github.com/tendermint/basecoin/modules/auth/commands" + basecmd "github.com/tendermint/basecoin/modules/base/commands" + coincmd "github.com/tendermint/basecoin/modules/coin/commands" + feecmd "github.com/tendermint/basecoin/modules/fee/commands" + noncecmd "github.com/tendermint/basecoin/modules/nonce/commands" ) // BaseCli - main basecoin client command @@ -30,6 +36,15 @@ tmcli to work for any custom abci app. `, } +// VersionCmd - command to show the application version +var VersionCmd = &cobra.Command{ + Use: "version", + Short: "Show version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version.Version) + }, +} + func main() { commands.AddBasicFlags(BaseCli) @@ -38,15 +53,24 @@ func main() { // These are default parsers, but optional in your app (you can remove key) proofs.TxCmd, proofs.KeyCmd, - bcmd.AccountQueryCmd, - bcmd.NonceQueryCmd, + coincmd.AccountQueryCmd, + noncecmd.NonceQueryCmd, ) + // set up the middleware + bcmd.Middleware = bcmd.Wrappers{ + feecmd.FeeWrapper{}, + noncecmd.NonceWrapper{}, + basecmd.ChainWrapper{}, + authcmd.SigWrapper{}, + } + bcmd.Middleware.Register(txs.RootCmd.PersistentFlags()) + // you will always want this for the base send command proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{}) txs.RootCmd.AddCommand( // This is the default transaction, optional in your app - bcmd.SendTxCmd, + coincmd.SendTxCmd, ) // Set up the various commands to use @@ -59,7 +83,7 @@ func main() { proofs.RootCmd, txs.RootCmd, proxy.RootCmd, - coincmd.VersionCmd, + VersionCmd, bcmd.AutoCompleteCmd, ) diff --git a/docs/guide/counter/cmd/countercli/commands/counter.go b/docs/guide/counter/cmd/countercli/commands/counter.go index 68fb867006..22e56cf0ac 100644 --- a/docs/guide/counter/cmd/countercli/commands/counter.go +++ b/docs/guide/counter/cmd/countercli/commands/counter.go @@ -9,7 +9,6 @@ import ( "github.com/tendermint/basecoin" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" "github.com/tendermint/basecoin/docs/guide/counter/plugins/counter" - "github.com/tendermint/basecoin/modules/auth" "github.com/tendermint/basecoin/modules/coin" ) @@ -34,45 +33,23 @@ func init() { fs := CounterTxCmd.Flags() fs.String(FlagCountFee, "", "Coins to send in the format ,...") fs.Bool(FlagValid, false, "Is count valid?") - - fs.String(bcmd.FlagFee, "0mycoin", "Coins for the transaction fee of the format ") - fs.Int(bcmd.FlagSequence, -1, "Sequence number for this transaction") } // TODO: counterTx is very similar to the sendtx one, // maybe we can pull out some common patterns? func counterTx(cmd *cobra.Command, args []string) error { - // load data from json or flags - var tx basecoin.Tx - found, err := txcmd.LoadJSON(&tx) - if err != nil { - return err - } - if !found { - tx, err = readCounterTxFlags() - } + tx, err := readCounterTxFlags() if err != nil { return err } - // TODO: make this more flexible for middleware - tx, err = bcmd.WrapFeeTx(tx) + tx, err = bcmd.Middleware.Wrap(tx) if err != nil { return err } - tx, err = bcmd.WrapNonceTx(tx) - if err != nil { - return err - } - tx, err = bcmd.WrapChainTx(tx) - if err != nil { - return err - } - - stx := auth.NewSig(tx) // Sign if needed and post. This it the work-horse - bres, err := txcmd.SignAndPostTx(stx) + bres, err := txcmd.SignAndPostTx(tx.Unwrap()) if err != nil { return err } diff --git a/docs/guide/counter/cmd/countercli/main.go b/docs/guide/counter/cmd/countercli/main.go index 7b543d3526..8d5ae608bc 100644 --- a/docs/guide/counter/cmd/countercli/main.go +++ b/docs/guide/counter/cmd/countercli/main.go @@ -15,6 +15,11 @@ import ( bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" bcount "github.com/tendermint/basecoin/docs/guide/counter/cmd/countercli/commands" + authcmd "github.com/tendermint/basecoin/modules/auth/commands" + basecmd "github.com/tendermint/basecoin/modules/base/commands" + coincmd "github.com/tendermint/basecoin/modules/coin/commands" + feecmd "github.com/tendermint/basecoin/modules/fee/commands" + noncecmd "github.com/tendermint/basecoin/modules/nonce/commands" ) // BaseCli represents the base command when called without any subcommands @@ -37,17 +42,27 @@ func main() { // These are default parsers, optional in your app proofs.TxCmd, proofs.KeyCmd, - bcmd.AccountQueryCmd, + coincmd.AccountQueryCmd, + noncecmd.NonceQueryCmd, // XXX IMPORTANT: here is how you add custom query commands in your app bcount.CounterQueryCmd, ) + // set up the middleware + bcmd.Middleware = bcmd.Wrappers{ + feecmd.FeeWrapper{}, + noncecmd.NonceWrapper{}, + basecmd.ChainWrapper{}, + authcmd.SigWrapper{}, + } + bcmd.Middleware.Register(txs.RootCmd.PersistentFlags()) + // Prepare transactions proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{}) txs.RootCmd.AddCommand( // This is the default transaction, optional in your app - bcmd.SendTxCmd, + coincmd.SendTxCmd, // XXX IMPORTANT: here is how you add custom tx construction for your app bcount.CounterTxCmd, diff --git a/modules/auth/commands/wrap.go b/modules/auth/commands/wrap.go new file mode 100644 index 0000000000..a02033c654 --- /dev/null +++ b/modules/auth/commands/wrap.go @@ -0,0 +1,35 @@ +package commands + +import ( + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/tendermint/basecoin" + bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + "github.com/tendermint/basecoin/modules/auth" +) + +//nolint +const ( + FlagMulti = "multi" +) + +// SigWrapper wraps a tx with a signature layer to hold pubkey sigs +type SigWrapper struct{} + +var _ bcmd.Wrapper = SigWrapper{} + +// Wrap will wrap the tx with OneSig or MultiSig depending on flags +func (SigWrapper) Wrap(tx basecoin.Tx) (res basecoin.Tx, err error) { + if !viper.GetBool(FlagMulti) { + res = auth.NewSig(tx).Wrap() + } else { + res = auth.NewMulti(tx).Wrap() + } + return +} + +// Register adds the sequence flags to the cli +func (SigWrapper) Register(fs *pflag.FlagSet) { + fs.Bool(FlagMulti, false, "Prepare the tx for multisig") +} diff --git a/modules/base/commands/wrap.go b/modules/base/commands/wrap.go new file mode 100644 index 0000000000..a1042eb765 --- /dev/null +++ b/modules/base/commands/wrap.go @@ -0,0 +1,40 @@ +package commands + +import ( + "errors" + + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/tendermint/light-client/commands" + + "github.com/tendermint/basecoin" + bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + "github.com/tendermint/basecoin/modules/base" +) + +//nolint +const ( + FlagExpires = "expires" +) + +// ChainWrapper wraps a tx with an chain info and optional expiration +type ChainWrapper struct{} + +var _ bcmd.Wrapper = ChainWrapper{} + +// Wrap will wrap the tx with a ChainTx from the standard flags +func (ChainWrapper) Wrap(tx basecoin.Tx) (res basecoin.Tx, err error) { + expires := viper.GetInt64(FlagExpires) + chain := commands.GetChainID() + if chain == "" { + return res, errors.New("No chain-id provided") + } + res = base.NewChainTx(chain, uint64(expires), tx) + return +} + +// Register adds the sequence flags to the cli +func (ChainWrapper) Register(fs *pflag.FlagSet) { + fs.Uint64(FlagExpires, 0, "Block height at which this tx expires") +} diff --git a/modules/coin/commands/query.go b/modules/coin/commands/query.go new file mode 100644 index 0000000000..9a1308b2bb --- /dev/null +++ b/modules/coin/commands/query.go @@ -0,0 +1,39 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" + + lc "github.com/tendermint/light-client" + lcmd "github.com/tendermint/light-client/commands" + proofcmd "github.com/tendermint/light-client/commands/proofs" + + "github.com/tendermint/basecoin/modules/auth" + "github.com/tendermint/basecoin/modules/coin" + "github.com/tendermint/basecoin/stack" +) + +// AccountQueryCmd - command to query an account +var AccountQueryCmd = &cobra.Command{ + Use: "account [address]", + Short: "Get details of an account, with proof", + RunE: lcmd.RequireInit(doAccountQuery), +} + +func doAccountQuery(cmd *cobra.Command, args []string) error { + addr, err := proofcmd.ParseHexKey(args, "address") + if err != nil { + return err + } + key := stack.PrefixedKey(coin.NameCoin, auth.SigPerm(addr).Bytes()) + + acc := coin.Account{} + proof, err := proofcmd.GetAndParseAppProof(key, &acc) + if lc.IsNoDataErr(err) { + return errors.Errorf("Account bytes are empty for address %X ", addr) + } else if err != nil { + return err + } + + return proofcmd.OutputProof(acc, proof.BlockHeight()) +} diff --git a/modules/coin/commands/tx.go b/modules/coin/commands/tx.go new file mode 100644 index 0000000000..ce1c33a7fb --- /dev/null +++ b/modules/coin/commands/tx.go @@ -0,0 +1,89 @@ +package commands + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/light-client/commands" + txcmd "github.com/tendermint/light-client/commands/txs" + + "github.com/tendermint/basecoin" + bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + "github.com/tendermint/basecoin/modules/coin" +) + +// SendTxCmd is CLI command to send tokens between basecoin accounts +var SendTxCmd = &cobra.Command{ + Use: "send", + Short: "send tokens from one account to another", + RunE: commands.RequireInit(doSendTx), +} + +//nolint +const ( + FlagTo = "to" + FlagAmount = "amount" +) + +func init() { + flags := SendTxCmd.Flags() + flags.String(FlagTo, "", "Destination address for the bits") + flags.String(FlagAmount, "", "Coins to send in the format ,...") +} + +// doSendTx is an example of how to make a tx +func doSendTx(cmd *cobra.Command, args []string) error { + // load data from json or flags + // var tx basecoin.Tx + // found, err := txcmd.LoadJSON(&tx) + // if err != nil { + // return err + // } + + tx, err := readSendTxFlags() + if err != nil { + return err + } + + tx, err = bcmd.Middleware.Wrap(tx) + if err != nil { + return err + } + + // Sign if needed and post. This it the work-horse + bres, err := txcmd.SignAndPostTx(tx.Unwrap()) + if err != nil { + return err + } + if err = bcmd.ValidateResult(bres); err != nil { + return err + } + + // Output result + return txcmd.OutputTx(bres) +} + +func readSendTxFlags() (tx basecoin.Tx, err error) { + // parse to address + toAddr, err := bcmd.ParseAddress(viper.GetString(FlagTo)) + if err != nil { + return tx, err + } + + amountCoins, err := coin.ParseCoins(viper.GetString(FlagAmount)) + if err != nil { + return tx, err + } + + // craft the inputs and outputs + ins := []coin.TxInput{{ + Address: bcmd.GetSignerAct(), + Coins: amountCoins, + }} + outs := []coin.TxOutput{{ + Address: toAddr, + Coins: amountCoins, + }} + + return coin.NewSendTx(ins, outs), nil +} diff --git a/modules/fee/commands/wrap.go b/modules/fee/commands/wrap.go new file mode 100644 index 0000000000..deb4f42ef1 --- /dev/null +++ b/modules/fee/commands/wrap.go @@ -0,0 +1,42 @@ +package commands + +import ( + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/tendermint/basecoin" + bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + "github.com/tendermint/basecoin/modules/coin" + "github.com/tendermint/basecoin/modules/fee" +) + +//nolint +const ( + FlagFee = "fee" +) + +// FeeWrapper wraps a tx with an optional fee payment +type FeeWrapper struct{} + +var _ bcmd.Wrapper = FeeWrapper{} + +// Wrap checks for FlagFee and if present wraps the tx with a +// FeeTx of the given amount, paid by the signer +func (FeeWrapper) Wrap(tx basecoin.Tx) (res basecoin.Tx, err error) { + //parse the fee and amounts into coin types + toll, err := coin.ParseCoin(viper.GetString(FlagFee)) + if err != nil { + return res, err + } + // if no fee, do nothing, otherwise wrap it + if toll.IsZero() { + return tx, nil + } + res = fee.NewFee(tx, toll, bcmd.GetSignerAct()) + return +} + +// Register adds the sequence flags to the cli +func (FeeWrapper) Register(fs *pflag.FlagSet) { + fs.String(FlagFee, "0mycoin", "Coins for the transaction fee of the format ") +} diff --git a/modules/nonce/commands/query.go b/modules/nonce/commands/query.go new file mode 100644 index 0000000000..233de59e21 --- /dev/null +++ b/modules/nonce/commands/query.go @@ -0,0 +1,46 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" + + lc "github.com/tendermint/light-client" + lcmd "github.com/tendermint/light-client/commands" + proofcmd "github.com/tendermint/light-client/commands/proofs" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/modules/auth" + "github.com/tendermint/basecoin/modules/nonce" + "github.com/tendermint/basecoin/stack" +) + +// NonceQueryCmd - command to query an nonce account +var NonceQueryCmd = &cobra.Command{ + Use: "nonce [address]", + Short: "Get details of a nonce sequence number, with proof", + RunE: lcmd.RequireInit(doNonceQuery), +} + +func doNonceQuery(cmd *cobra.Command, args []string) error { + addr, err := proofcmd.ParseHexKey(args, "address") + if err != nil { + return err + } + + act := []basecoin.Actor{basecoin.NewActor( + auth.NameSigs, + addr, + )} + + key := stack.PrefixedKey(nonce.NameNonce, nonce.GetSeqKey(act)) + + var seq uint32 + proof, err := proofcmd.GetAndParseAppProof(key, &seq) + if lc.IsNoDataErr(err) { + return errors.Errorf("Sequence is empty for address %X ", addr) + } else if err != nil { + return err + } + + return proofcmd.OutputProof(seq, proof.BlockHeight()) +} diff --git a/modules/nonce/commands/wrap.go b/modules/nonce/commands/wrap.go new file mode 100644 index 0000000000..533462b9fc --- /dev/null +++ b/modules/nonce/commands/wrap.go @@ -0,0 +1,41 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/tendermint/basecoin" + bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + "github.com/tendermint/basecoin/modules/nonce" +) + +// nolint +const ( + FlagSequence = "sequence" +) + +// NonceWrapper wraps a tx with a nonce +type NonceWrapper struct{} + +var _ bcmd.Wrapper = NonceWrapper{} + +// Wrap grabs the sequence number from the flag and wraps +// the tx with this nonce. Grabs the permission from the signer, +// as we still only support single sig on the cli +func (NonceWrapper) Wrap(tx basecoin.Tx) (res basecoin.Tx, err error) { + //add the nonce tx layer to the tx + seq := viper.GetInt(FlagSequence) + if seq < 0 { + return res, fmt.Errorf("sequence must be greater than 0") + } + signers := []basecoin.Actor{bcmd.GetSignerAct()} + res = nonce.NewTx(uint32(seq), signers, tx) + return +} + +// Register adds the sequence flags to the cli +func (NonceWrapper) Register(fs *pflag.FlagSet) { + fs.Int(FlagSequence, -1, "Sequence number for this transaction") +}