From 8f67b6be84e7a5309c66ffbfe1bda737ff69b0fe Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 15 Jun 2017 13:36:18 +0200 Subject: [PATCH] Got counter tx working, needs testing --- cmd/basecli/commands/apptx.go | 39 ++++++++ cmd/basecli/commands/{adapters.go => cmds.go} | 85 ++++++---------- cmd/basecli/counter/counter.go | 99 ++++++++++--------- cmd/basecli/main.go | 5 +- 4 files changed, 125 insertions(+), 103 deletions(-) rename cmd/basecli/commands/{adapters.go => cmds.go} (64%) diff --git a/cmd/basecli/commands/apptx.go b/cmd/basecli/commands/apptx.go index 7cf9e3c6a2..1e68600e17 100644 --- a/cmd/basecli/commands/apptx.go +++ b/cmd/basecli/commands/apptx.go @@ -57,3 +57,42 @@ func (s *AppTx) TxBytes() ([]byte, error) { txBytes := wire.BinaryBytes(bc.TxS{s.Tx}) return txBytes, nil } + +// AddSigner sets address and pubkey info on the tx based on the key that +// will be used for signing +func (a *AppTx) AddSigner(pk crypto.PubKey) { + // get addr if available + var addr []byte + if !pk.Empty() { + addr = pk.Address() + } + + // set the send address, and pubkey if needed + in := &a.Tx.Input + in.Address = addr + if in.Sequence == 1 { + in.PubKey = pk + } +} + +// TODO: this should really be in the basecoin.types SendTx, +// but that code is too ugly now, needs refactor.. +func (a *AppTx) ValidateBasic() error { + if a.chainID == "" { + return errors.New("No chainId specified") + } + in := a.Tx.Input + if len(in.Address) != 20 { + return errors.Errorf("Invalid input address length: %d", len(in.Address)) + } + if !in.Coins.IsValid() { + return errors.Errorf("Invalid input coins %v", in.Coins) + } + if in.Coins.IsZero() { + return errors.New("Input coins cannot be zero") + } + if in.Sequence <= 0 { + return errors.New("Sequence must be greater than 0") + } + return nil +} diff --git a/cmd/basecli/commands/adapters.go b/cmd/basecli/commands/cmds.go similarity index 64% rename from cmd/basecli/commands/adapters.go rename to cmd/basecli/commands/cmds.go index f9bfe0f44b..268f4d61d0 100644 --- a/cmd/basecli/commands/adapters.go +++ b/cmd/basecli/commands/cmds.go @@ -8,7 +8,6 @@ import ( flag "github.com/spf13/pflag" "github.com/spf13/viper" - crypto "github.com/tendermint/go-crypto" "github.com/tendermint/light-client/commands" txcmd "github.com/tendermint/light-client/commands/txs" @@ -71,7 +70,7 @@ func doSendTx(cmd *cobra.Command, args []string) error { func readSendTxFlags(tx *btypes.SendTx) error { // parse to address - to, err := hex.DecodeString(StripHex(viper.GetString(ToFlag))) + to, err := ParseHexFlag(ToFlag) if err != nil { return errors.Errorf("To address is invalid hex: %v\n", err) } @@ -104,76 +103,52 @@ func readSendTxFlags(tx *btypes.SendTx) error { /******** AppTx *********/ -type AppFlags struct { - Fee string - Gas int64 - Amount string - Sequence int +func AddAppTxFlags(fs *flag.FlagSet) { + fs.String(AmountFlag, "", "Coins to send in the format ,...") + fs.String(FeeFlag, "0mycoin", "Coins for the transaction fee of the format ") + fs.Int64(GasFlag, 0, "Amount of gas for this transaction") + fs.Int(SequenceFlag, -1, "Sequence number for this transaction") } -func AppFlagSet() (*flag.FlagSet, AppFlags) { - fs := flag.NewFlagSet("", flag.ContinueOnError) - - fs.String("amount", "", "Coins to send in the format ,...") - fs.String("fee", "", "Coins for the transaction fee of the format ") - fs.Int64("gas", 0, "Amount of gas for this transaction") - fs.Int("sequence", -1, "Sequence number for this transaction") - return fs, AppFlags{} -} - -// AppTxReader allows us to create AppTx -type AppTxReader struct { - ChainID string -} - -func (t AppTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) { - return nil, errors.New("Not implemented...") -} - -func (t AppTxReader) ReadTxFlags(data *AppFlags, app string, appData []byte, pk crypto.PubKey) (interface{}, error) { +// ReadAppTxFlags reads in the standard flags +// your command should parse info to set tx.Name and tx.Data +func ReadAppTxFlags(tx *btypes.AppTx) error { //parse the fee and amounts into coin types - feeCoin, err := btypes.ParseCoin(data.Fee) + var err error + tx.Fee, err = btypes.ParseCoin(viper.GetString(FeeFlag)) if err != nil { - return nil, err + return err } - amountCoins, err := btypes.ParseCoins(data.Amount) + amountCoins, err := btypes.ParseCoins(viper.GetString(AmountFlag)) if err != nil { - return nil, err + return err } - // get addr if available - var addr []byte - if !pk.Empty() { - addr = pk.Address() - } + // set the gas + tx.Gas = viper.GetInt64(GasFlag) - // craft the tx - input := btypes.TxInput{ - Address: addr, + // craft the inputs and outputs + tx.Input = btypes.TxInput{ Coins: amountCoins, - Sequence: data.Sequence, - } - if data.Sequence == 1 { - input.PubKey = pk - } - tx := btypes.AppTx{ - Gas: data.Gas, - Fee: feeCoin, - Input: input, - Name: app, - Data: appData, + Sequence: viper.GetInt(SequenceFlag), } - // wrap it in the proper signer thing... - send := AppTx{ - chainID: t.ChainID, - Tx: &tx, + return nil +} + +func WrapAppTx(tx *btypes.AppTx) *AppTx { + return &AppTx{ + chainID: viper.GetString(commands.ChainFlag), + Tx: tx, } - return &send, nil } /** TODO copied from basecoin cli - put in common somewhere? **/ +func ParseHexFlag(flag string) ([]byte, error) { + return hex.DecodeString(StripHex(viper.GetString(flag))) +} + // Returns true for non-empty hex-string prefixed with "0x" func isHex(s string) bool { if len(s) > 2 && s[:2] == "0x" { diff --git a/cmd/basecli/counter/counter.go b/cmd/basecli/counter/counter.go index 9ec931091e..b39792e2ce 100644 --- a/cmd/basecli/counter/counter.go +++ b/cmd/basecli/counter/counter.go @@ -1,72 +1,79 @@ package counter import ( - flag "github.com/spf13/pflag" + "github.com/spf13/cobra" "github.com/spf13/viper" - crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" - lightclient "github.com/tendermint/light-client" - "github.com/tendermint/light-client/commands" - "github.com/tendermint/light-client/commands/txs" + txcmd "github.com/tendermint/light-client/commands/txs" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" "github.com/tendermint/basecoin/plugins/counter" btypes "github.com/tendermint/basecoin/types" ) -/**** build out the tx ****/ +var CounterTxCmd = &cobra.Command{ + Use: "counter", + Short: "add a vote to the counter", + Long: `Add a vote to the counter. -var ( - _ txs.ReaderMaker = CounterTxMaker{} - _ lightclient.TxReader = CounterTxReader{} +You must pass --valid for it to count and the countfee will be added to the counter.`, + RunE: doCounterTx, +} + +const ( + CountFeeFlag = "countfee" + ValidFlag = "valid" ) -type CounterTxMaker struct{} - -func (m CounterTxMaker) MakeReader() (lightclient.TxReader, error) { - chainID := viper.GetString(commands.ChainFlag) - return CounterTxReader{bcmd.AppTxReader{ChainID: chainID}}, nil +func init() { + fs := CounterTxCmd.Flags() + bcmd.AddAppTxFlags(fs) + fs.String(CountFeeFlag, "", "Coins to send in the format ,...") + fs.Bool(ValidFlag, false, "Is count valid?") } -// define flags +func doCounterTx(cmd *cobra.Command, args []string) error { + tx := new(btypes.AppTx) + // Note: we don't support loading apptx from json currently, so skip that -type CounterFlags struct { - bcmd.AppFlags `mapstructure:",squash"` - Valid bool - CountFee string -} - -func (m CounterTxMaker) Flags() (*flag.FlagSet, interface{}) { - fs, app := bcmd.AppFlagSet() - fs.String("countfee", "", "Coins to send in the format ,...") - fs.Bool("valid", false, "Is count valid?") - return fs, &CounterFlags{AppFlags: app} -} - -// parse flags - -type CounterTxReader struct { - App bcmd.AppTxReader -} - -func (t CounterTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) { - // TODO: something. maybe? - return t.App.ReadTxJSON(data, pk) -} - -func (t CounterTxReader) ReadTxFlags(flags interface{}, pk crypto.PubKey) (interface{}, error) { - data := flags.(*CounterFlags) - countFee, err := btypes.ParseCoins(data.CountFee) + // read the standard flags + err := bcmd.ReadAppTxFlags(tx) if err != nil { - return nil, err + return err } + // now read the app-specific flags + err = readCounterFlags(tx) + if err != nil { + return err + } + + app := bcmd.WrapAppTx(tx) + app.AddSigner(txcmd.GetSigner()) + + // Sign if needed and post. This it the work-horse + bres, err := txcmd.SignAndPostTx(app) + if err != nil { + return err + } + + // output result + return txcmd.OutputTx(bres) +} + +// readCounterFlags sets the app-specific data in the AppTx +func readCounterFlags(tx *btypes.AppTx) error { + countFee, err := btypes.ParseCoins(viper.GetString(CountFeeFlag)) + if err != nil { + return err + } ctx := counter.CounterTx{ - Valid: viper.GetBool("valid"), + Valid: viper.GetBool(ValidFlag), Fee: countFee, } - txBytes := wire.BinaryBytes(ctx) - return t.App.ReadTxFlags(&data.AppFlags, counter.New().Name(), txBytes, pk) + tx.Name = counter.New().Name() + tx.Data = wire.BinaryBytes(ctx) + return nil } diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go index 9b58d12046..d5116d1600 100644 --- a/cmd/basecli/main.go +++ b/cmd/basecli/main.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tmlibs/cli" bcmd "github.com/tendermint/basecoin/cmd/basecli/commands" + bcount "github.com/tendermint/basecoin/cmd/basecli/counter" ) // BaseCli represents the base command when called without any subcommands @@ -37,13 +38,13 @@ func main() { pr.AddCommand(proofs.TxCmd) pr.AddCommand(proofs.KeyCmd) pr.AddCommand(bcmd.AccountQueryCmd) - // pr.AddCommand(bcount.CounterQueryCmd) + pr.AddCommand(bcount.CounterQueryCmd) // here is how you would add the custom txs... but don't really add demo in your app proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{}) tr := txs.RootCmd tr.AddCommand(bcmd.SendTxCmd) - // tr.AddCommand(bcmd.AppTxCmd) + tr.AddCommand(bcount.CounterTxCmd) // TODO