diff --git a/cmd/basecoin/main.go b/cmd/basecoin/main.go index 4eb4c944c5..bcb5493010 100644 --- a/cmd/basecoin/main.go +++ b/cmd/basecoin/main.go @@ -4,17 +4,18 @@ import ( "fmt" "os" + "github.com/spf13/cobra" + "github.com/tendermint/basecoin/cmd/commands" - "github.com/tendermint/basecoin/version" - "github.com/urfave/cli" ) func main() { - app := cli.NewApp() - app.Name = "basecoin" - app.Usage = "basecoin [command] [args...]" - app.Version = version.Version - app.Commands = []cli.Command{ + var RootCmd = &cobra.Command{ + Use: "basecoin", + Short: "A cryptocurrency framework in Golang based on Tendermint-Core", + } + + RootCmd.AddCommand( commands.InitCmd, commands.StartCmd, commands.TxCmd, @@ -24,9 +25,10 @@ func main() { commands.BlockCmd, commands.AccountCmd, commands.UnsafeResetAllCmd, - } - err := app.Run(os.Args) - if err != nil { + commands.VersionCmd, + ) + + if err := RootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) } diff --git a/cmd/commands/flags.go b/cmd/commands/flags.go deleted file mode 100644 index ccf95769d3..0000000000 --- a/cmd/commands/flags.go +++ /dev/null @@ -1,119 +0,0 @@ -package commands - -import ( - "github.com/urfave/cli" -) - -// start flags -var ( - AddrFlag = cli.StringFlag{ - Name: "address", - Value: "tcp://0.0.0.0:46658", - Usage: "Listen address", - } - - EyesFlag = cli.StringFlag{ - Name: "eyes", - Value: "local", - Usage: "MerkleEyes address, or 'local' for embedded", - } - - // TODO: move to config file - // eyesCacheSizePtr := flag.Int("eyes-cache-size", 10000, "MerkleEyes db cache size, for embedded") - - WithoutTendermintFlag = cli.BoolFlag{ - Name: "without-tendermint", - Usage: "Run the Basecoin app without Tendermint", - } -) - -// tx flags - -var ( - NodeFlag = cli.StringFlag{ - Name: "node", - Value: "tcp://localhost:46657", - Usage: "Tendermint RPC address", - } - - ToFlag = cli.StringFlag{ - Name: "to", - Value: "", - Usage: "Destination address for the transaction", - } - - AmountFlag = cli.StringFlag{ - Name: "amount", - Value: "", - Usage: "Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver)", - } - - FromFlag = cli.StringFlag{ - Name: "from", - Value: "key.json", - Usage: "Path to a private key to sign the transaction", - } - - SeqFlag = cli.IntFlag{ - Name: "sequence", - Value: 0, - Usage: "Sequence number for the account", - } - - GasFlag = cli.IntFlag{ - Name: "gas", - Value: 0, - Usage: "The amount of gas for the transaction", - } - - FeeFlag = cli.StringFlag{ - Name: "fee", - Value: "", - Usage: "Coins for the transaction fee of the format ", - } - - DataFlag = cli.StringFlag{ - Name: "data", - Value: "", - Usage: "Data to send with the transaction", - } - - NameFlag = cli.StringFlag{ - Name: "name", - Value: "", - Usage: "Plugin to send the transaction to", - } - - ChainIDFlag = cli.StringFlag{ - Name: "chain_id", - Value: "test_chain_id", - Usage: "ID of the chain for replay protection", - } -) - -// proof flags -var ( - ProofFlag = cli.StringFlag{ - Name: "proof", - Usage: "hex-encoded IAVL proof", - Value: "", - } - - KeyFlag = cli.StringFlag{ - Name: "key", - Usage: "key to the IAVL tree", - Value: "", - } - - ValueFlag = cli.StringFlag{ - Name: "value", - Usage: "value in the IAVL tree", - Value: "", - } - - RootFlag = cli.StringFlag{ - Name: "root", - Usage: "root hash of the IAVL tree", - Value: "", - } -) diff --git a/cmd/commands/ibc.go b/cmd/commands/ibc.go index 34f4b7923d..48fd4d6c2b 100644 --- a/cmd/commands/ibc.go +++ b/cmd/commands/ibc.go @@ -2,11 +2,10 @@ package commands import ( "encoding/hex" - "errors" "fmt" "io/ioutil" - "github.com/urfave/cli" + "github.com/spf13/cobra" "github.com/tendermint/basecoin/plugins/ibc" @@ -21,172 +20,110 @@ func NewIBCPlugin() *ibc.IBCPlugin { return ibc.New() } -//--------------------------------------------------------------------- -// ibc flags - +//commands var ( - IbcChainIDFlag = cli.StringFlag{ - Name: "chain_id", - Usage: "ChainID for the new blockchain", - Value: "", + IBCTxCmd = &cobra.Command{ + Use: "ibc", + Short: "An IBC transaction, for InterBlockchain Communication", } - IbcGenesisFlag = cli.StringFlag{ - Name: "genesis", - Usage: "Genesis file for the new blockchain", - Value: "", + IBCRegisterTxCmd = &cobra.Command{ + Use: "register", + Short: "Register a blockchain via IBC", + Run: ibcRegisterTxCmd, } - IbcHeaderFlag = cli.StringFlag{ - Name: "header", - Usage: "Block header for an ibc update", - Value: "", + IBCUpdateTxCmd = &cobra.Command{ + Use: "update", + Short: "Update the latest state of a blockchain via IBC", + Run: ibcUpdateTxCmd, } - IbcCommitFlag = cli.StringFlag{ - Name: "commit", - Usage: "Block commit for an ibc update", - Value: "", + IBCPacketTxCmd = &cobra.Command{ + Use: "packet", + Short: "Send a new packet via IBC", } - IbcFromFlag = cli.StringFlag{ - Name: "from", - Usage: "Source ChainID", - Value: "", + IBCPacketCreateTxCmd = &cobra.Command{ + Use: "create", + Short: "Create an egress IBC packet", + Run: ibcPacketCreateTxCmd, } - IbcToFlag = cli.StringFlag{ - Name: "to", - Usage: "Destination ChainID", - Value: "", - } - - IbcTypeFlag = cli.StringFlag{ - Name: "type", - Usage: "IBC packet type (eg. coin)", - Value: "", - } - - IbcPayloadFlag = cli.StringFlag{ - Name: "payload", - Usage: "IBC packet payload", - Value: "", - } - - IbcPacketFlag = cli.StringFlag{ - Name: "packet", - Usage: "hex-encoded IBC packet", - Value: "", - } - - IbcProofFlag = cli.StringFlag{ - Name: "proof", - Usage: "hex-encoded proof of IBC packet from source chain", - Value: "", - } - - IbcSequenceFlag = cli.IntFlag{ - Name: "sequence", - Usage: "sequence number for IBC packet", - Value: 0, - } - - IbcHeightFlag = cli.IntFlag{ - Name: "height", - Usage: "Height the packet became egress in source chain", - Value: 0, + IBCPacketPostTxCmd = &cobra.Command{ + Use: "post", + Short: "Deliver an IBC packet to another chain", + Run: ibcPacketPostTxCmd, } ) -//--------------------------------------------------------------------- -// ibc commands - +//flags var ( - IbcTxCmd = cli.Command{ - Name: "ibc", - Usage: "an IBC transaction, for InterBlockchain Communication", - Flags: TxFlags, - Subcommands: []cli.Command{ - IbcRegisterTxCmd, - IbcUpdateTxCmd, - IbcPacketTxCmd, - }, - } - - IbcRegisterTxCmd = cli.Command{ - Name: "register", - Usage: "Register a blockchain via IBC", - Action: func(c *cli.Context) error { - return cmdIBCRegisterTx(c) - }, - Flags: []cli.Flag{ - IbcChainIDFlag, - IbcGenesisFlag, - }, - } - - IbcUpdateTxCmd = cli.Command{ - Name: "update", - Usage: "Update the latest state of a blockchain via IBC", - Action: func(c *cli.Context) error { - return cmdIBCUpdateTx(c) - }, - Flags: []cli.Flag{ - IbcHeaderFlag, - IbcCommitFlag, - }, - } - - IbcPacketTxCmd = cli.Command{ - Name: "packet", - Usage: "Send a new packet via IBC", - Subcommands: []cli.Command{ - IbcPacketCreateTx, - IbcPacketPostTx, - }, - } - - IbcPacketCreateTx = cli.Command{ - Name: "create", - Usage: "Create an egress IBC packet", - Action: func(c *cli.Context) error { - return cmdIBCPacketCreateTx(c) - }, - Flags: []cli.Flag{ - IbcFromFlag, - IbcToFlag, - IbcTypeFlag, - IbcPayloadFlag, - IbcSequenceFlag, - }, - } - - IbcPacketPostTx = cli.Command{ - Name: "post", - Usage: "Deliver an IBC packet to another chain", - Action: func(c *cli.Context) error { - return cmdIBCPacketPostTx(c) - }, - Flags: []cli.Flag{ - IbcFromFlag, - IbcHeightFlag, - IbcPacketFlag, - IbcProofFlag, - }, - } + ibcChainIDFlag string + ibcGenesisFlag string + ibcHeaderFlag string + ibcCommitFlag string + ibcFromFlag string + ibcToFlag string + ibcTypeFlag string + ibcPayloadFlag string + ibcPacketFlag string + ibcProofFlag string + ibcSequenceFlag int + ibcHeightFlag int ) +func init() { + + // register flags + registerFlags := []Flag2Register{ + {&ibcChainIDFlag, "ibc_chain_id", "", "ChainID for the new blockchain"}, + {&ibcGenesisFlag, "genesis", "", "Genesis file for the new blockchain"}, + } + + updateFlags := []Flag2Register{ + {&ibcHeaderFlag, "header", "", "Block header for an ibc update"}, + {&ibcCommitFlag, "commit", "", "Block commit for an ibc update"}, + } + + fromFlagReg := Flag2Register{&ibcFromFlag, "ibc_from", "", "Source ChainID"} + + packetCreateFlags := []Flag2Register{ + fromFlagReg, + {&ibcToFlag, "to", "", "Destination ChainID"}, + {&ibcTypeFlag, "type", "", "IBC packet type (eg. coin},"}, + {&ibcPayloadFlag, "payload", "", "IBC packet payload"}, + {&ibcSequenceFlag, "ibc_sequence", -1, "sequence number for IBC packet"}, + } + + packetPostFlags := []Flag2Register{ + fromFlagReg, + {&ibcHeightFlag, "height", 0, "Height the packet became egress in source chain"}, + {&ibcPacketFlag, "packet", "", "hex-encoded IBC packet"}, + {&ibcProofFlag, "proof", "", "hex-encoded proof of IBC packet from source chain"}, + } + + RegisterFlags(IBCRegisterTxCmd, registerFlags) + RegisterFlags(IBCUpdateTxCmd, updateFlags) + RegisterFlags(IBCPacketCreateTxCmd, packetCreateFlags) + RegisterFlags(IBCPacketPostTxCmd, packetPostFlags) + + //register commands + IBCTxCmd.AddCommand(IBCRegisterTxCmd, IBCUpdateTxCmd, IBCPacketTxCmd) + IBCPacketTxCmd.AddCommand(IBCPacketCreateTxCmd, IBCPacketPostTxCmd) + RegisterTxSubcommand(IBCTxCmd) +} + //--------------------------------------------------------------------- // ibc command implementations -func cmdIBCRegisterTx(c *cli.Context) error { - chainID := c.String("chain_id") - genesisFile := c.String("genesis") - parent := c.Parent() +func ibcRegisterTxCmd(cmd *cobra.Command, args []string) { + chainID := ibcChainIDFlag + genesisFile := ibcGenesisFlag genesisBytes, err := ioutil.ReadFile(genesisFile) if err != nil { - return errors.New(cmn.Fmt("Error reading genesis file %v: %v", genesisFile, err)) + cmn.Exit(fmt.Sprintf("Error reading genesis file %v: %+v\n", genesisFile, err)) } ibcTx := ibc.IBCRegisterChainTx{ @@ -203,27 +140,31 @@ func cmdIBCRegisterTx(c *cli.Context) error { }{ibcTx})) name := "IBC" - return AppTx(parent, name, data) + AppTx(name, data) } -func cmdIBCUpdateTx(c *cli.Context) error { - headerBytes, err := hex.DecodeString(StripHex(c.String("header"))) +func ibcUpdateTxCmd(cmd *cobra.Command, args []string) { + headerBytes, err := hex.DecodeString(StripHex(ibcHeaderFlag)) if err != nil { - return errors.New(cmn.Fmt("Header (%v) is invalid hex: %v", c.String("header"), err)) + cmn.Exit(fmt.Sprintf("Header (%v) is invalid hex: %+v\n", ibcHeaderFlag, err)) } - commitBytes, err := hex.DecodeString(StripHex(c.String("commit"))) + + commitBytes, err := hex.DecodeString(StripHex(ibcCommitFlag)) if err != nil { - return errors.New(cmn.Fmt("Commit (%v) is invalid hex: %v", c.String("commit"), err)) + cmn.Exit(fmt.Sprintf("Commit (%v) is invalid hex: %+v\n", ibcCommitFlag, err)) } header := new(tmtypes.Header) commit := new(tmtypes.Commit) - if err := wire.ReadBinaryBytes(headerBytes, &header); err != nil { - return errors.New(cmn.Fmt("Error unmarshalling header: %v", err)) + err = wire.ReadBinaryBytes(headerBytes, &header) + if err != nil { + cmn.Exit(fmt.Sprintf("Error unmarshalling header: %+v\n", err)) } - if err := wire.ReadBinaryBytes(commitBytes, &commit); err != nil { - return errors.New(cmn.Fmt("Error unmarshalling commit: %v", err)) + + err = wire.ReadBinaryBytes(commitBytes, &commit) + if err != nil { + cmn.Exit(fmt.Sprintf("Error unmarshalling commit: %+v\n", err)) } ibcTx := ibc.IBCUpdateChainTx{ @@ -238,21 +179,21 @@ func cmdIBCUpdateTx(c *cli.Context) error { }{ibcTx})) name := "IBC" - return AppTx(c.Parent(), name, data) + AppTx(name, data) } -func cmdIBCPacketCreateTx(c *cli.Context) error { - fromChain, toChain := c.String("from"), c.String("to") - packetType := c.String("type") +func ibcPacketCreateTxCmd(cmd *cobra.Command, args []string) { + fromChain, toChain := ibcFromFlag, ibcToFlag + packetType := ibcTypeFlag - payloadBytes, err := hex.DecodeString(StripHex(c.String("payload"))) + payloadBytes, err := hex.DecodeString(StripHex(ibcPayloadFlag)) if err != nil { - return errors.New(cmn.Fmt("Payload (%v) is invalid hex: %v", c.String("payload"), err)) + cmn.Exit(fmt.Sprintf("Payload (%v) is invalid hex: %+v\n", ibcPayloadFlag, err)) } - sequence, err := getIBCSequence(c) + sequence, err := ibcSequenceCmd() if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } ibcTx := ibc.IBCPacketCreateTx{ @@ -271,29 +212,33 @@ func cmdIBCPacketCreateTx(c *cli.Context) error { ibc.IBCTx `json:"unwrap"` }{ibcTx})) - return AppTx(c.Parent().Parent(), "IBC", data) + AppTx("IBC", data) } -func cmdIBCPacketPostTx(c *cli.Context) error { - fromChain, fromHeight := c.String("from"), c.Int("height") +func ibcPacketPostTxCmd(cmd *cobra.Command, args []string) { + fromChain, fromHeight := ibcFromFlag, ibcHeightFlag - packetBytes, err := hex.DecodeString(StripHex(c.String("packet"))) + packetBytes, err := hex.DecodeString(StripHex(ibcPacketFlag)) if err != nil { - return errors.New(cmn.Fmt("Packet (%v) is invalid hex: %v", c.String("packet"), err)) + cmn.Exit(fmt.Sprintf("Packet (%v) is invalid hex: %+v\n", ibcPacketFlag, err)) } - proofBytes, err := hex.DecodeString(StripHex(c.String("proof"))) + + proofBytes, err := hex.DecodeString(StripHex(ibcProofFlag)) if err != nil { - return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err)) + cmn.Exit(fmt.Sprintf("Proof (%v) is invalid hex: %+v\n", ibcProofFlag, err)) } var packet ibc.Packet proof := new(merkle.IAVLProof) - if err := wire.ReadBinaryBytes(packetBytes, &packet); err != nil { - return errors.New(cmn.Fmt("Error unmarshalling packet: %v", err)) + err = wire.ReadBinaryBytes(packetBytes, &packet) + if err != nil { + cmn.Exit(fmt.Sprintf("Error unmarshalling packet: %+v\n", err)) } - if err := wire.ReadBinaryBytes(proofBytes, &proof); err != nil { - return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err)) + + err = wire.ReadBinaryBytes(proofBytes, &proof) + if err != nil { + cmn.Exit(fmt.Sprintf("Error unmarshalling proof: %+v\n", err)) } ibcTx := ibc.IBCPacketPostTx{ @@ -309,12 +254,12 @@ func cmdIBCPacketPostTx(c *cli.Context) error { ibc.IBCTx `json:"unwrap"` }{ibcTx})) - return AppTx(c.Parent().Parent(), "IBC", data) + AppTx("IBC", data) } -func getIBCSequence(c *cli.Context) (uint64, error) { - if c.IsSet("sequence") { - return uint64(c.Int("sequence")), nil +func ibcSequenceCmd() (uint64, error) { + if ibcSequenceFlag >= 0 { + return uint64(ibcSequenceFlag), nil } // TODO: get sequence diff --git a/cmd/commands/init.go b/cmd/commands/init.go index e60e7d1306..99bff657c8 100644 --- a/cmd/commands/init.go +++ b/cmd/commands/init.go @@ -1,27 +1,38 @@ package commands import ( + "fmt" "io/ioutil" "path" - "github.com/urfave/cli" + "github.com/spf13/cobra" cmn "github.com/tendermint/go-common" ) -var InitCmd = cli.Command{ - Name: "init", - Usage: "Initialize a basecoin blockchain", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdInit(c) - }, - Flags: []cli.Flag{ - ChainIDFlag, - }, -} +//commands +var ( + InitCmd = &cobra.Command{ + Use: "init", + Short: "Initialize a basecoin blockchain", + Run: initCmd, + } +) -func cmdInit(c *cli.Context) error { +//flags +//var chainIDFlag string + +//func init() { +// +// //register flags +// flags := []Flag2Register{ +// {&chainIDFlag, "chain_id", "test_chain_id", "ID of the chain for replay protection"}, +// } +// RegisterFlags(InitCmd, flags) +// +//} + +func initCmd(cmd *cobra.Command, args []string) { rootDir := BasecoinRoot("") cmn.EnsureDir(rootDir, 0777) @@ -32,22 +43,27 @@ func cmdInit(c *cli.Context) error { key1File := path.Join(rootDir, "key.json") key2File := path.Join(rootDir, "key2.json") - if err := ioutil.WriteFile(genesisFile, []byte(genesisJSON), 0644); err != nil { - return err + err := ioutil.WriteFile(genesisFile, []byte(genesisJSON), 0644) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } - if err := ioutil.WriteFile(privValFile, []byte(privValJSON), 0400); err != nil { - return err + + err = ioutil.WriteFile(privValFile, []byte(privValJSON), 0400) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } - if err := ioutil.WriteFile(key1File, []byte(key1JSON), 0400); err != nil { - return err + + err = ioutil.WriteFile(key1File, []byte(key1JSON), 0400) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } - if err := ioutil.WriteFile(key2File, []byte(key2JSON), 0400); err != nil { - return err + + err = ioutil.WriteFile(key2File, []byte(key2JSON), 0400) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File) - - return nil } const privValJSON = `{ diff --git a/cmd/commands/key.go b/cmd/commands/key.go index 124f390ddf..a79ca92f16 100644 --- a/cmd/commands/key.go +++ b/cmd/commands/key.go @@ -8,38 +8,38 @@ import ( "path" "strings" - "github.com/urfave/cli" + "github.com/spf13/cobra" cmn "github.com/tendermint/go-common" "github.com/tendermint/go-crypto" ) +//commands var ( - KeyCmd = cli.Command{ - Name: "key", - Usage: "Manage keys", - ArgsUsage: "", - Subcommands: []cli.Command{NewKeyCmd}, + KeyCmd = &cobra.Command{ + Use: "key", + Short: "Manage keys", } - NewKeyCmd = cli.Command{ - Name: "new", - Usage: "Create a new private key", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdNewKey(c) - }, + NewKeyCmd = &cobra.Command{ + Use: "new", + Short: "Create a new private key", + Run: newKeyCmd, } ) -func cmdNewKey(c *cli.Context) error { +func newKeyCmd(cmd *cobra.Command, args []string) { key := genKey() keyJSON, err := json.MarshalIndent(key, "", "\t") if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } fmt.Println(string(keyJSON)) - return nil +} + +func init() { + //register commands + KeyCmd.AddCommand(NewKeyCmd) } //--------------------------------------------- @@ -88,12 +88,14 @@ func LoadKey(keyFile string) *Key { filePath := path.Join(BasecoinRoot(""), keyFile) keyJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { - cmn.Exit(err.Error()) + cmn.Exit(fmt.Sprintf("%+v\n", err)) } + key := new(Key) err = json.Unmarshal(keyJSONBytes, key) if err != nil { - cmn.Exit(cmn.Fmt("Error reading key from %v: %v\n", filePath, err)) + cmn.Exit(fmt.Sprintf("Error reading key from %v: %v\n", filePath, err)) } + return key } diff --git a/cmd/commands/plugin_util.go b/cmd/commands/plugin_util.go new file mode 100644 index 0000000000..082ff2669f --- /dev/null +++ b/cmd/commands/plugin_util.go @@ -0,0 +1,41 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/tendermint/basecoin/types" +) + +type plugin struct { + name string + newPlugin func() types.Plugin +} + +var plugins = []plugin{} + +// RegisterStartPlugin is used to enable a plugin +func RegisterStartPlugin(name string, newPlugin func() types.Plugin) { + plugins = append(plugins, plugin{name: name, newPlugin: newPlugin}) +} + +// Register a subcommand of QueryCmd for plugin specific query functionality +func RegisterQuerySubcommand(cmd *cobra.Command) { + QueryCmd.AddCommand(cmd) +} + +// Register a subcommand of TxCmd to craft transactions for plugins +func RegisterTxSubcommand(cmd *cobra.Command) { + TxCmd.AddCommand(cmd) +} + +//Returns a version command based on version input +func QuickVersionCmd(version string) *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Show version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version) + }, + } +} diff --git a/cmd/commands/query.go b/cmd/commands/query.go index 231d32f9fb..7e6e8de285 100644 --- a/cmd/commands/query.go +++ b/cmd/commands/query.go @@ -2,11 +2,10 @@ package commands import ( "encoding/hex" - "errors" "fmt" "strconv" - "github.com/urfave/cli" + "github.com/spf13/cobra" cmn "github.com/tendermint/go-common" "github.com/tendermint/go-merkle" @@ -14,85 +13,85 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) +//commands var ( - QueryCmd = cli.Command{ - Name: "query", - Usage: "Query the merkle tree", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdQuery(c) - }, - Flags: []cli.Flag{ - NodeFlag, - }, + QueryCmd = &cobra.Command{ + Use: "query [key]", + Short: "Query the merkle tree", + Run: queryCmd, } - AccountCmd = cli.Command{ - Name: "account", - Usage: "Get details of an account", - ArgsUsage: "
", - Action: func(c *cli.Context) error { - return cmdAccount(c) - }, - Flags: []cli.Flag{ - NodeFlag, - }, + AccountCmd = &cobra.Command{ + Use: "account [address]", + Short: "Get details of an account", + Run: accountCmd, } - BlockCmd = cli.Command{ - Name: "block", - Usage: "Get the header and commit of a block", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdBlock(c) - }, - Flags: []cli.Flag{ - NodeFlag, - }, + BlockCmd = &cobra.Command{ + Use: "block [height]", + Short: "Get the header and commit of a block", + Run: blockCmd, } - VerifyCmd = cli.Command{ - Name: "verify", - Usage: "Verify the IAVL proof", - Action: func(c *cli.Context) error { - return cmdVerify(c) - }, - Flags: []cli.Flag{ - ProofFlag, - KeyFlag, - ValueFlag, - RootFlag, - }, + VerifyCmd = &cobra.Command{ + Use: "verify", + Short: "Verify the IAVL proof", + Run: verifyCmd, } ) -// Register a subcommand of QueryCmd for plugin specific query functionality -func RegisterQuerySubcommand(cmd cli.Command) { - QueryCmd.Subcommands = append(QueryCmd.Subcommands, cmd) +//flags +var ( + nodeFlag string + proofFlag string + keyFlag string + valueFlag string + rootFlag string +) + +func init() { + + commonFlags := []Flag2Register{ + {&nodeFlag, "node", "tcp://localhost:46657", "Tendermint RPC address"}, + } + + verifyFlags := []Flag2Register{ + {&proofFlag, "proof", "", "hex-encoded IAVL proof"}, + {&keyFlag, "key", "", "key to the IAVL tree"}, + {&valueFlag, "value", "", "value in the IAVL tree"}, + {&rootFlag, "root", "", "root hash of the IAVL tree"}, + } + + RegisterFlags(QueryCmd, commonFlags) + RegisterFlags(AccountCmd, commonFlags) + RegisterFlags(BlockCmd, commonFlags) + RegisterFlags(VerifyCmd, verifyFlags) } -func cmdQuery(c *cli.Context) error { - if len(c.Args()) != 1 { - return errors.New("query command requires an argument ([key])") +func queryCmd(cmd *cobra.Command, args []string) { + + if len(args) != 1 { + cmn.Exit("query command requires an argument ([key])") } - keyString := c.Args()[0] + + keyString := args[0] key := []byte(keyString) if isHex(keyString) { // convert key to bytes var err error key, err = hex.DecodeString(StripHex(keyString)) if err != nil { - return errors.New(cmn.Fmt("Query key (%v) is invalid hex: %v", keyString, err)) + cmn.Exit(fmt.Sprintf("Query key (%v) is invalid hex: %+v\n", keyString, err)) } } - resp, err := Query(c.String("node"), key) + resp, err := Query(nodeFlag, key) if err != nil { - return err + cmn.Exit(fmt.Sprintf("Query returns error: %+v\n", err)) } if !resp.Code.IsOK() { - return errors.New(cmn.Fmt("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log)) + cmn.Exit(fmt.Sprintf("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log)) } val := resp.Value @@ -104,43 +103,44 @@ func cmdQuery(c *cli.Context) error { Proof []byte `json:"proof"` Height uint64 `json:"height"` }{val, proof, height}))) - - return nil } -func cmdAccount(c *cli.Context) error { - if len(c.Args()) != 1 { - return errors.New("account command requires an argument ([address])") +func accountCmd(cmd *cobra.Command, args []string) { + + if len(args) != 1 { + cmn.Exit("account command requires an argument ([address])") } - addrHex := StripHex(c.Args()[0]) + + addrHex := StripHex(args[0]) // convert destination address to bytes addr, err := hex.DecodeString(addrHex) if err != nil { - return errors.New(cmn.Fmt("Account address (%v) is invalid hex: %v", addrHex, err)) + cmn.Exit(fmt.Sprintf("Account address (%v) is invalid hex: %+v\n", addrHex, err)) } - acc, err := getAcc(c.String("node"), addr) + acc, err := getAcc(nodeFlag, addr) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } fmt.Println(string(wire.JSONBytes(acc))) - return nil } -func cmdBlock(c *cli.Context) error { - if len(c.Args()) != 1 { - return errors.New("block command requires an argument ([height])") - } - heightString := c.Args()[0] - height, err := strconv.Atoi(heightString) - if err != nil { - return errors.New(cmn.Fmt("Height must be an int, got %v: %v", heightString, err)) +func blockCmd(cmd *cobra.Command, args []string) { + + if len(args) != 1 { + cmn.Exit("block command requires an argument ([height])") } - header, commit, err := getHeaderAndCommit(c, height) + heightString := args[0] + height, err := strconv.Atoi(heightString) if err != nil { - return err + cmn.Exit(fmt.Sprintf("Height must be an int, got %v: %+v\n", heightString, err)) + } + + header, commit, err := getHeaderAndCommit(nodeFlag, height) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } fmt.Println(string(wire.JSONBytes(struct { @@ -156,8 +156,6 @@ func cmdBlock(c *cli.Context) error { Commit: commit, }, }))) - - return nil } type BlockHex struct { @@ -170,15 +168,16 @@ type BlockJSON struct { Commit *tmtypes.Commit `json:"commit"` } -func cmdVerify(c *cli.Context) error { - keyString, valueString := c.String("key"), c.String("value") +func verifyCmd(cmd *cobra.Command, args []string) { + + keyString, valueString := keyFlag, valueFlag var err error key := []byte(keyString) if isHex(keyString) { key, err = hex.DecodeString(StripHex(keyString)) if err != nil { - return errors.New(cmn.Fmt("Key (%v) is invalid hex: %v", keyString, err)) + cmn.Exit(fmt.Sprintf("Key (%v) is invalid hex: %+v\n", keyString, err)) } } @@ -186,29 +185,25 @@ func cmdVerify(c *cli.Context) error { if isHex(valueString) { value, err = hex.DecodeString(StripHex(valueString)) if err != nil { - return errors.New(cmn.Fmt("Value (%v) is invalid hex: %v", valueString, err)) + cmn.Exit(fmt.Sprintf("Value (%v) is invalid hex: %+v\n", valueString, err)) } } - root, err := hex.DecodeString(StripHex(c.String("root"))) + root, err := hex.DecodeString(StripHex(rootFlag)) if err != nil { - return errors.New(cmn.Fmt("Root (%v) is invalid hex: %v", c.String("root"), err)) + cmn.Exit(fmt.Sprintf("Root (%v) is invalid hex: %+v\n", rootFlag, err)) } - proofBytes, err := hex.DecodeString(StripHex(c.String("proof"))) - if err != nil { - return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err)) - } + proofBytes, err := hex.DecodeString(StripHex(proofFlag)) proof, err := merkle.ReadProof(proofBytes) if err != nil { - return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err)) + cmn.Exit(fmt.Sprintf("Error unmarshalling proof: %+v\n", err)) } if proof.Verify(key, value, root) { fmt.Println("OK") } else { - return errors.New("Proof does not verify") + cmn.Exit(fmt.Sprintf("Proof does not verify")) } - return nil } diff --git a/cmd/commands/reset.go b/cmd/commands/reset.go index d9688fd859..f2841ea801 100644 --- a/cmd/commands/reset.go +++ b/cmd/commands/reset.go @@ -1,47 +1,24 @@ package commands import ( - "os" "path" - "github.com/urfave/cli" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/cmd/tendermint/commands" tmcfg "github.com/tendermint/tendermint/config/tendermint" - types "github.com/tendermint/tendermint/types" ) -var UnsafeResetAllCmd = cli.Command{ - Name: "unsafe_reset_all", - Usage: "Reset all blockchain data", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdUnsafeResetAll(c) - }, +var UnsafeResetAllCmd = &cobra.Command{ + Use: "unsafe_reset_all", + Short: "Reset all blockchain data", + Run: unsafeResetAllCmd, } -func cmdUnsafeResetAll(c *cli.Context) error { +func unsafeResetAllCmd(cmd *cobra.Command, args []string) { basecoinDir := BasecoinRoot("") tmDir := path.Join(basecoinDir) tmConfig := tmcfg.GetConfig(tmDir) - // Get and Reset PrivValidator - var privValidator *types.PrivValidator - privValidatorFile := tmConfig.GetString("priv_validator_file") - if _, err := os.Stat(privValidatorFile); err == nil { - privValidator = types.LoadPrivValidator(privValidatorFile) - privValidator.Reset() - log.Notice("Reset PrivValidator", "file", privValidatorFile) - } else { - privValidator = types.GenPrivValidator() - privValidator.SetFile(privValidatorFile) - privValidator.Save() - log.Notice("Generated PrivValidator", "file", privValidatorFile) - } - - // Remove all tendermint data - tmDataDir := tmConfig.GetString("db_dir") - os.RemoveAll(tmDataDir) - log.Notice("Removed all data", "dir", tmDataDir) - - return nil + commands.ResetAll(tmConfig, log) } diff --git a/cmd/commands/start.go b/cmd/commands/start.go index 1e6861ac06..d14f663d12 100644 --- a/cmd/commands/start.go +++ b/cmd/commands/start.go @@ -1,12 +1,11 @@ package commands import ( - "errors" "fmt" "os" "path" - "github.com/urfave/cli" + "github.com/spf13/cobra" "github.com/tendermint/abci/server" cmn "github.com/tendermint/go-common" @@ -18,50 +17,51 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/tendermint/basecoin/app" - "github.com/tendermint/basecoin/types" ) +var StartCmd = &cobra.Command{ + Use: "start", + Short: "Start basecoin", + Run: startCmd, +} + +//flags +var ( + addrFlag string + eyesFlag string + dirFlag string + withoutTendermintFlag bool +) + +// TODO: move to config file const EyesCacheSize = 10000 -var StartCmd = cli.Command{ - Name: "start", - Usage: "Start basecoin", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdStart(c) - }, - Flags: []cli.Flag{ - AddrFlag, - EyesFlag, - WithoutTendermintFlag, - ChainIDFlag, - }, +func init() { + + flags := []Flag2Register{ + {&addrFlag, "address", "tcp://0.0.0.0:46658", "Listen address"}, + {&eyesFlag, "eyes", "local", "MerkleEyes address, or 'local' for embedded"}, + {&dirFlag, "dir", ".", "Root directory"}, + {&withoutTendermintFlag, "without-tendermint", false, "Run Tendermint in-process with the App"}, + } + RegisterFlags(StartCmd, flags) + + // TODO: move to config file + // eyesCacheSizePtr := flag.Int("eyes-cache-size", 10000, "MerkleEyes db cache size, for embedded") } -type plugin struct { - name string - newPlugin func() types.Plugin -} - -var plugins = []plugin{} - -// RegisterStartPlugin is used to enable a plugin -func RegisterStartPlugin(name string, newPlugin func() types.Plugin) { - plugins = append(plugins, plugin{name: name, newPlugin: newPlugin}) -} - -func cmdStart(c *cli.Context) error { +func startCmd(cmd *cobra.Command, args []string) { basecoinDir := BasecoinRoot("") // Connect to MerkleEyes var eyesCli *eyes.Client - if c.String("eyes") == "local" { + if eyesFlag == "local" { eyesCli = eyes.NewLocalClient(path.Join(basecoinDir, "data", "merkleeyes.db"), EyesCacheSize) } else { var err error - eyesCli, err = eyes.NewClient(c.String("eyes")) + eyesCli, err = eyes.NewClient(eyesFlag) if err != nil { - return errors.New("connect to MerkleEyes: " + err.Error()) + cmn.Exit(fmt.Sprintf("Error connecting to MerkleEyes: %+v\n", err)) } } @@ -84,7 +84,7 @@ func cmdStart(c *cli.Context) error { if _, err := os.Stat(genesisFile); err == nil { err := basecoinApp.LoadGenesis(genesisFile) if err != nil { - return errors.New(cmn.Fmt("%+v", err)) + cmn.Exit(fmt.Sprintf("Error in LoadGenesis: %+v\n", err)) } } else { fmt.Printf("No genesis file at %s, skipping...\n", genesisFile) @@ -92,40 +92,37 @@ func cmdStart(c *cli.Context) error { } chainID := basecoinApp.GetState().GetChainID() - if c.Bool("without-tendermint") { + if withoutTendermintFlag { log.Notice("Starting Basecoin without Tendermint", "chain_id", chainID) // run just the abci app/server - return startBasecoinABCI(c, basecoinApp) + startBasecoinABCI(basecoinApp) } else { log.Notice("Starting Basecoin with Tendermint", "chain_id", chainID) // start the app with tendermint in-process - return startTendermint(basecoinDir, basecoinApp) + startTendermint(basecoinDir, basecoinApp) } - - return nil } -func startBasecoinABCI(c *cli.Context, basecoinApp *app.Basecoin) error { +func startBasecoinABCI(basecoinApp *app.Basecoin) { + // Start the ABCI listener - svr, err := server.NewServer(c.String("address"), "socket", basecoinApp) + svr, err := server.NewServer(addrFlag, "socket", basecoinApp) if err != nil { - return errors.New("create listener: " + err.Error()) + cmn.Exit(fmt.Sprintf("Error creating listener: %+v\n", err)) } + // Wait forever cmn.TrapSignal(func() { // Cleanup svr.Stop() }) - return nil - } -func startTendermint(dir string, basecoinApp *app.Basecoin) error { +func startTendermint(dir string, basecoinApp *app.Basecoin) { + // Get configuration tmConfig := tmcfg.GetConfig(dir) - // logger.SetLogLevel("notice") //config.GetString("log_level")) - // parseFlags(config, args[1:]) // Command line overrides // Create & start tendermint node @@ -135,7 +132,7 @@ func startTendermint(dir string, basecoinApp *app.Basecoin) error { _, err := n.Start() if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } // Wait forever @@ -143,6 +140,4 @@ func startTendermint(dir string, basecoinApp *app.Basecoin) error { // Cleanup n.Stop() }) - - return nil } diff --git a/cmd/commands/tx.go b/cmd/commands/tx.go index 338bbcb22b..95358221f4 100644 --- a/cmd/commands/tx.go +++ b/cmd/commands/tx.go @@ -2,10 +2,10 @@ package commands import ( "encoding/hex" - "errors" "fmt" - "github.com/urfave/cli" + "github.com/pkg/errors" + "github.com/spf13/cobra" "github.com/tendermint/basecoin/types" crypto "github.com/tendermint/go-crypto" @@ -16,211 +16,215 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -var TxFlags = []cli.Flag{ - NodeFlag, - ChainIDFlag, - - FromFlag, - - AmountFlag, - GasFlag, - FeeFlag, - SeqFlag, -} - +//commands var ( - TxCmd = cli.Command{ - Name: "tx", - Usage: "Create, sign, and broadcast a transaction", - ArgsUsage: "", - Subcommands: []cli.Command{ - SendTxCmd, - AppTxCmd, - IbcTxCmd, - }, + TxCmd = &cobra.Command{ + Use: "tx", + Short: "Create, sign, and broadcast a transaction", } - SendTxCmd = cli.Command{ - Name: "send", - Usage: "a SendTx transaction, for sending tokens around", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdSendTx(c) - }, - Flags: append(TxFlags, ToFlag), + SendTxCmd = &cobra.Command{ + Use: "send", + Short: "A SendTx transaction, for sending tokens around", + Run: sendTxCmd, } - AppTxCmd = cli.Command{ - Name: "app", - Usage: "an AppTx transaction, for sending raw data to plugins", - ArgsUsage: "", - Action: func(c *cli.Context) error { - return cmdAppTx(c) - }, - Flags: append(TxFlags, NameFlag, DataFlag), - // Subcommands are dynamically registered with plugins as needed - Subcommands: []cli.Command{}, + AppTxCmd = &cobra.Command{ + Use: "app", + Short: "An AppTx transaction, for sending raw data to plugins", + Run: appTxCmd, } ) -// Register a subcommand of TxCmd to craft transactions for plugins -func RegisterTxSubcommand(cmd cli.Command) { - TxCmd.Subcommands = append(TxCmd.Subcommands, cmd) +//flags +var ( + txNodeFlag string + toFlag string + amountFlag string + fromFlag string + seqFlag int + gasFlag int + feeFlag string + dataFlag string + nameFlag string + chainIDFlag string +) + +func init() { + + // register flags + cmdTxFlags := []Flag2Register{ + {&txNodeFlag, "node", "tcp://localhost:46657", "Tendermint RPC address"}, + {&chainIDFlag, "chain_id", "test_chain_id", "ID of the chain for replay protection"}, + {&fromFlag, "from", "key.json", "Path to a private key to sign the transaction"}, + {&amountFlag, "amount", "", "Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver},"}, + {&gasFlag, "gas", 0, "The amount of gas for the transaction"}, + {&feeFlag, "fee", "", "Coins for the transaction fee of the format "}, + {&seqFlag, "sequence", -1, "Sequence number for the account (-1 to autocalculate},"}, + } + + sendTxFlags := []Flag2Register{ + {&toFlag, "to", "", "Destination address for the transaction"}, + } + + appTxFlags := []Flag2Register{ + {&nameFlag, "name", "", "Plugin to send the transaction to"}, + {&dataFlag, "data", "", "Data to send with the transaction"}, + } + + RegisterPersistentFlags(TxCmd, cmdTxFlags) + RegisterFlags(SendTxCmd, sendTxFlags) + RegisterFlags(AppTxCmd, appTxFlags) + + //register commands + TxCmd.AddCommand(SendTxCmd, AppTxCmd) } -func cmdSendTx(c *cli.Context) error { - toHex := c.String("to") - fromFile := c.String("from") - amount := c.String("amount") - gas := int64(c.Int("gas")) - fee := c.String("fee") - chainID := c.String("chain_id") +func sendTxCmd(cmd *cobra.Command, args []string) { // convert destination address to bytes - to, err := hex.DecodeString(StripHex(toHex)) + to, err := hex.DecodeString(StripHex(toFlag)) if err != nil { - return errors.New("To address is invalid hex: " + err.Error()) + cmn.Exit(fmt.Sprintf("To address is invalid hex: %+v\n", err)) } // load the priv key - privKey := LoadKey(fromFile) + privKey := LoadKey(fromFlag) // get the sequence number for the tx - sequence, err := getSeq(c, privKey.Address[:]) + sequence, err := getSeq(privKey.Address[:]) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } //parse the fee and amounts into coin types - feeCoin, err := types.ParseCoin(fee) + feeCoin, err := types.ParseCoin(feeFlag) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } - amountCoins, err := types.ParseCoins(amount) + amountCoins, err := types.ParseCoins(amountFlag) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } // craft the tx input := types.NewTxInput(privKey.PubKey, amountCoins, sequence) output := newOutput(to, amountCoins) tx := &types.SendTx{ - Gas: gas, + Gas: int64(gasFlag), Fee: feeCoin, Inputs: []types.TxInput{input}, Outputs: []types.TxOutput{output}, } // sign that puppy - signBytes := tx.SignBytes(chainID) + signBytes := tx.SignBytes(chainIDFlag) tx.Inputs[0].Signature = crypto.SignatureS{privKey.Sign(signBytes)} fmt.Println("Signed SendTx:") fmt.Println(string(wire.JSONBytes(tx))) // broadcast the transaction to tendermint - data, log, err := broadcastTx(c, tx) + data, log, err := broadcastTx(tx) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } fmt.Printf("Response: %X ; %s\n", data, log) - return nil } -func cmdAppTx(c *cli.Context) error { +func appTxCmd(cmd *cobra.Command, args []string) { // convert data to bytes - dataString := c.String("data") - data := []byte(dataString) - if isHex(dataString) { - data, _ = hex.DecodeString(dataString) + data := []byte(dataFlag) + if isHex(dataFlag) { + data, _ = hex.DecodeString(dataFlag) } - name := c.String("name") - return AppTx(c, name, data) + name := nameFlag + AppTx(name, data) } -func AppTx(c *cli.Context, name string, data []byte) error { - fromFile := c.String("from") - amount := c.String("amount") - fee := c.String("fee") - gas := int64(c.Int("gas")) - chainID := c.String("chain_id") +func AppTx(name string, data []byte) { - privKey := LoadKey(fromFile) + privKey := LoadKey(fromFlag) - sequence, err := getSeq(c, privKey.Address[:]) + sequence, err := getSeq(privKey.Address[:]) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } //parse the fee and amounts into coin types - feeCoin, err := types.ParseCoin(fee) + feeCoin, err := types.ParseCoin(feeFlag) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } - amountCoins, err := types.ParseCoins(amount) + + amountCoins, err := types.ParseCoins(amountFlag) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } input := types.NewTxInput(privKey.PubKey, amountCoins, sequence) tx := &types.AppTx{ - Gas: gas, + Gas: int64(gasFlag), Fee: feeCoin, Name: name, Input: input, Data: data, } - tx.Input.Signature = crypto.SignatureS{privKey.Sign(tx.SignBytes(chainID))} + tx.Input.Signature = crypto.SignatureS{privKey.Sign(tx.SignBytes(chainIDFlag))} fmt.Println("Signed AppTx:") fmt.Println(string(wire.JSONBytes(tx))) - data, log, err := broadcastTx(c, tx) + data, log, err := broadcastTx(tx) if err != nil { - return err + cmn.Exit(fmt.Sprintf("%+v\n", err)) } fmt.Printf("Response: %X ; %s\n", data, log) - - return nil } // broadcast the transaction to tendermint -func broadcastTx(c *cli.Context, tx types.Tx) ([]byte, string, error) { +func broadcastTx(tx types.Tx) ([]byte, string, error) { + tmResult := new(ctypes.TMResult) - tmAddr := c.String("node") - uriClient := client.NewURIClient(tmAddr) + uriClient := client.NewURIClient(txNodeFlag) // Don't you hate having to do this? // How many times have I lost an hour over this trick?! txBytes := []byte(wire.BinaryBytes(struct { types.Tx `json:"unwrap"` }{tx})) + _, err := uriClient.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult) if err != nil { return nil, "", errors.New(cmn.Fmt("Error on broadcast tx: %v", err)) } + res := (*tmResult).(*ctypes.ResultBroadcastTxCommit) + // if it fails check, we don't even get a delivertx back! if !res.CheckTx.Code.IsOK() { r := res.CheckTx return nil, "", errors.New(cmn.Fmt("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)) } + if !res.DeliverTx.Code.IsOK() { r := res.DeliverTx return nil, "", errors.New(cmn.Fmt("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)) } + return res.DeliverTx.Data, res.DeliverTx.Log, nil } // if the sequence flag is set, return it; // else, fetch the account by querying the app and return the sequence number -func getSeq(c *cli.Context, address []byte) (int, error) { - if c.IsSet("sequence") { - return c.Int("sequence"), nil +func getSeq(address []byte) (int, error) { + + if seqFlag >= 0 { + return seqFlag, nil } - tmAddr := c.String("node") - acc, err := getAcc(tmAddr, address) + + acc, err := getAcc(txNodeFlag, address) if err != nil { return 0, err } @@ -232,5 +236,4 @@ func newOutput(to []byte, amount types.Coins) types.TxOutput { Address: to, Coins: amount, } - } diff --git a/cmd/commands/utils.go b/cmd/commands/utils.go index efff67e0bf..f49bfe162d 100644 --- a/cmd/commands/utils.go +++ b/cmd/commands/utils.go @@ -2,16 +2,17 @@ package commands import ( "encoding/hex" - "errors" + "fmt" "os" - "github.com/urfave/cli" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/types" abci "github.com/tendermint/abci/types" - cmn "github.com/tendermint/go-common" client "github.com/tendermint/go-rpc/client" wire "github.com/tendermint/go-wire" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -28,6 +29,60 @@ func BasecoinRoot(rootDir string) string { return rootDir } +type Flag2Register struct { + Pointer interface{} + Use string + Value interface{} + Desc string +} + +//register flag utils +func RegisterFlags(c *cobra.Command, flags []Flag2Register) { + registerFlags(c, flags, false) +} + +func RegisterPersistentFlags(c *cobra.Command, flags []Flag2Register) { + registerFlags(c, flags, true) +} + +func registerFlags(c *cobra.Command, flags []Flag2Register, persistent bool) { + + var flagset *pflag.FlagSet + if persistent { + flagset = c.PersistentFlags() + } else { + flagset = c.Flags() + } + + for _, f := range flags { + + ok := false + + switch f.Value.(type) { + case string: + if _, ok = f.Pointer.(*string); ok { + flagset.StringVar(f.Pointer.(*string), f.Use, f.Value.(string), f.Desc) + } + case int: + if _, ok = f.Pointer.(*int); ok { + flagset.IntVar(f.Pointer.(*int), f.Use, f.Value.(int), f.Desc) + } + case uint64: + if _, ok = f.Pointer.(*uint64); ok { + flagset.Uint64Var(f.Pointer.(*uint64), f.Use, f.Value.(uint64), f.Desc) + } + case bool: + if _, ok = f.Pointer.(*bool); ok { + flagset.BoolVar(f.Pointer.(*bool), f.Use, f.Value.(bool), f.Desc) + } + } + + if !ok { + panic("improper use of RegisterFlags") + } + } +} + // Returns true for non-empty hex-string prefixed with "0x" func isHex(s string) bool { if len(s) > 2 && s[:2] == "0x" { @@ -58,11 +113,11 @@ func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) { } _, err := uriClient.Call("abci_query", params, tmResult) if err != nil { - return nil, errors.New(cmn.Fmt("Error calling /abci_query: %v", err)) + return nil, errors.New(fmt.Sprintf("Error calling /abci_query: %v", err)) } res := (*tmResult).(*ctypes.ResultABCIQuery) if !res.Response.Code.IsOK() { - return nil, errors.New(cmn.Fmt("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log)) + return nil, errors.New(fmt.Sprintf("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log)) } return &res.Response, nil } @@ -79,28 +134,27 @@ func getAcc(tmAddr string, address []byte) (*types.Account, error) { accountBytes := response.Value if len(accountBytes) == 0 { - return nil, errors.New(cmn.Fmt("Account bytes are empty for address: %X ", address)) + return nil, errors.New(fmt.Sprintf("Account bytes are empty for address: %X ", address)) } var acc *types.Account err = wire.ReadBinaryBytes(accountBytes, &acc) if err != nil { - return nil, errors.New(cmn.Fmt("Error reading account %X error: %v", + return nil, errors.New(fmt.Sprintf("Error reading account %X error: %v", accountBytes, err.Error())) } return acc, nil } -func getHeaderAndCommit(c *cli.Context, height int) (*tmtypes.Header, *tmtypes.Commit, error) { +func getHeaderAndCommit(tmAddr string, height int) (*tmtypes.Header, *tmtypes.Commit, error) { tmResult := new(ctypes.TMResult) - tmAddr := c.String("node") uriClient := client.NewURIClient(tmAddr) method := "commit" _, err := uriClient.Call(method, map[string]interface{}{"height": height}, tmResult) if err != nil { - return nil, nil, errors.New(cmn.Fmt("Error on %s: %v", method, err)) + return nil, nil, errors.New(fmt.Sprintf("Error on %s: %v", method, err)) } resCommit := (*tmResult).(*ctypes.ResultCommit) header := resCommit.Header diff --git a/cmd/commands/version.go b/cmd/commands/version.go new file mode 100644 index 0000000000..1cbf891e43 --- /dev/null +++ b/cmd/commands/version.go @@ -0,0 +1,17 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/tendermint/basecoin/version" +) + +var VersionCmd = &cobra.Command{ + Use: "version", + Short: "Show version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version.Version) + }, +} diff --git a/cmd/counter/cmd.go b/cmd/counter/cmd.go index c125feb4f6..6b31e3fc61 100644 --- a/cmd/counter/cmd.go +++ b/cmd/counter/cmd.go @@ -3,46 +3,47 @@ package main import ( "fmt" + "github.com/spf13/cobra" wire "github.com/tendermint/go-wire" - "github.com/urfave/cli" "github.com/tendermint/basecoin/cmd/commands" "github.com/tendermint/basecoin/plugins/counter" "github.com/tendermint/basecoin/types" + cmn "github.com/tendermint/go-common" +) + +//commands +var CounterTxCmd = &cobra.Command{ + Use: "counter", + Short: "Create, sign, and broadcast a transaction to the counter plugin", + Run: counterTxCmd, +} + +//flags +var ( + validFlag bool + countFeeFlag string ) func init() { + + CounterTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set valid field in CounterTx") + CounterTxCmd.Flags().StringVar(&countFeeFlag, "countfee", "", "Coins for the counter fee of the format ") + commands.RegisterTxSubcommand(CounterTxCmd) commands.RegisterStartPlugin("counter", func() types.Plugin { return counter.New() }) } -var ( - ValidFlag = cli.BoolFlag{ - Name: "valid", - Usage: "Set valid field in CounterTx", - } +func counterTxCmd(cmd *cobra.Command, args []string) { - CounterTxCmd = cli.Command{ - Name: "counter", - Usage: "Create, sign, and broadcast a transaction to the counter plugin", - Action: func(c *cli.Context) error { - return cmdCounterTx(c) - }, - Flags: append(commands.TxFlags, ValidFlag), + countFee, err := commands.ParseCoins(countFeeFlag) + if err != nil { + cmn.Exit(fmt.Sprintf("%+v\n", err)) } -) - -func cmdCounterTx(c *cli.Context) error { - valid := c.Bool("valid") counterTx := counter.CounterTx{ - Valid: valid, - Fee: types.Coins{ - { - Denom: c.String("coin"), - Amount: int64(c.Int("fee")), - }, - }, + Valid: validFlag, + Fee: countFee, } fmt.Println("CounterTx:", string(wire.JSONBytes(counterTx))) @@ -50,5 +51,5 @@ func cmdCounterTx(c *cli.Context) error { data := wire.BinaryBytes(counterTx) name := "counter" - return commands.AppTx(c, name, data) + commands.AppTx(name, data) } diff --git a/cmd/counter/main.go b/cmd/counter/main.go index 72395a9b07..fc5ad6b02a 100644 --- a/cmd/counter/main.go +++ b/cmd/counter/main.go @@ -1,23 +1,34 @@ package main import ( + "fmt" "os" + "github.com/spf13/cobra" + "github.com/tendermint/basecoin/cmd/commands" - "github.com/urfave/cli" ) func main() { - app := cli.NewApp() - app.Name = "counter" - app.Usage = "counter [command] [args...]" - app.Version = "0.1.0" - app.Commands = []cli.Command{ + + var RootCmd = &cobra.Command{ + Use: "counter", + Short: "demo plugin for basecoin", + } + + RootCmd.AddCommand( commands.StartCmd, commands.TxCmd, - commands.KeyCmd, commands.QueryCmd, + commands.KeyCmd, + commands.VerifyCmd, + commands.BlockCmd, commands.AccountCmd, + commands.QuickVersionCmd("0.1.0"), + ) + + if err := RootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) } - app.Run(os.Args) } diff --git a/demo/start.sh b/demo/start.sh index af91db2a07..d7d9659dc4 100644 --- a/demo/start.sh +++ b/demo/start.sh @@ -103,14 +103,14 @@ sleep 3 echo "... registering chain1 on chain2" echo "" # register chain1 on chain2 -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --chain_id $CHAIN_ID1 --genesis ./data/chain1/genesis.json +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis ./data/chain1/genesis.json echo "" echo "... creating egress packet on chain1" echo "" # create a packet on chain1 destined for chain2 PAYLOAD="DEADBEEF" #TODO -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --sequence 1 +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 1 echo "" echo "... querying for packet data" @@ -162,7 +162,7 @@ echo "" echo "... posting packet from chain1 on chain2" echo "" # post the packet from chain1 to chain2 -basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF echo "" echo "... checking if the packet is present on chain2" diff --git a/docs/guide/example-plugin.md b/docs/guide/example-plugin.md index 31aa3479c2..312f3ac23d 100644 --- a/docs/guide/example-plugin.md +++ b/docs/guide/example-plugin.md @@ -25,18 +25,30 @@ The `main.go` is very simple and does not need to be changed: ```golang func main() { - app := cli.NewApp() - app.Name = "example-plugin" - app.Usage = "example-plugin [command] [args...]" - app.Version = "0.1.0" - app.Commands = []cli.Command{ + //Initialize example-plugin root command + var RootCmd = &cobra.Command{ + Use: "example-plugin", + Short: "example-plugin usage description", + } + + //Add the default basecoin commands to the root command + RootCmd.AddCommand( + commands.InitCmd, commands.StartCmd, commands.TxCmd, - commands.KeyCmd, commands.QueryCmd, + commands.KeyCmd, + commands.VerifyCmd, + commands.BlockCmd, commands.AccountCmd, + commands.UnsafeResetAllCmd, + ) + + //Run the root command + if err := RootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) } - app.Run(os.Args) } ``` @@ -49,19 +61,40 @@ This is where the `cmd.go` comes in. ### cmd.go -First, we register the plugin: +First we define the new command and associated flag variables +```golang +var ( + //CLI Flags + validFlag bool + + //CLI Plugin Commands + ExamplePluginTxCmd = &cobra.Command{ + Use: "example", + Short: "Create, sign, and broadcast a transaction to the example plugin", + Run: examplePluginTxCmd, + } +) +``` + +Next we register the plugin: ```golang func init() { + + //Set the Plugin Flags + ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid") + + //Register a plugin specific CLI command as a subcommand of the tx command commands.RegisterTxSubcommand(ExamplePluginTxCmd) + + //Register the example with basecoin at start commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() }) } ``` This creates a new subcommand under `tx` (defined below), and ensures the plugin is activated when we start the app. -Now we actually define the new command: ```golang var ( diff --git a/docs/guide/src/example-plugin/cmd.go b/docs/guide/src/example-plugin/cmd.go index 6d7c29a799..51077d06f5 100644 --- a/docs/guide/src/example-plugin/cmd.go +++ b/docs/guide/src/example-plugin/cmd.go @@ -1,16 +1,32 @@ package main import ( + "github.com/spf13/cobra" + wire "github.com/tendermint/go-wire" - "github.com/urfave/cli" "github.com/tendermint/basecoin/cmd/commands" "github.com/tendermint/basecoin/types" ) +var ( + //CLI Flags + validFlag bool + + //CLI Plugin Commands + ExamplePluginTxCmd = &cobra.Command{ + Use: "example", + Short: "Create, sign, and broadcast a transaction to the example plugin", + Run: examplePluginTxCmd, + } +) + //Called during CLI initialization func init() { + //Set the Plugin Flags + ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid") + //Register a plugin specific CLI command as a subcommand of the tx command commands.RegisterTxSubcommand(ExamplePluginTxCmd) @@ -18,32 +34,12 @@ func init() { commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() }) } -var ( - //CLI Flags - ExampleFlag = cli.BoolFlag{ - Name: "valid", - Usage: "Set this to make the transaction valid", - } - - //CLI Plugin Commands - ExamplePluginTxCmd = cli.Command{ - Name: "example", - Usage: "Create, sign, and broadcast a transaction to the example plugin", - Action: func(c *cli.Context) error { - return cmdExamplePluginTx(c) - }, - Flags: append(commands.TxFlags, ExampleFlag), - } -) - //Send a transaction -func cmdExamplePluginTx(c *cli.Context) error { - //Retrieve any flag results - exampleFlag := c.Bool("valid") +func examplePluginTxCmd(cmd *cobra.Command, args []string) { // Create a transaction using the flag. // The tx passes on custom information to the plugin - exampleTx := ExamplePluginTx{exampleFlag} + exampleTx := ExamplePluginTx{validFlag} // The tx is passed to the plugin in the form of // a byte array. This is achieved by serializing the object using go-wire. @@ -62,5 +58,5 @@ func cmdExamplePluginTx(c *cli.Context) error { // - Once deserialized, the tx is passed to `state.ExecTx` (state/execution.go) // - If the tx passes various checks, the `tx.Data` is forwarded as `txBytes` to `plugin.RunTx` (docs/guide/src/example-plugin/plugin.go) // - Finally, it deserialized back to the ExamplePluginTx - return commands.AppTx(c, "example-plugin", exampleTxBytes) + commands.AppTx("example-plugin", exampleTxBytes) } diff --git a/docs/guide/src/example-plugin/main.go b/docs/guide/src/example-plugin/main.go index d8cc38a958..3a2984b60d 100644 --- a/docs/guide/src/example-plugin/main.go +++ b/docs/guide/src/example-plugin/main.go @@ -1,24 +1,38 @@ package main import ( + "fmt" "os" + "github.com/spf13/cobra" + "github.com/tendermint/basecoin/cmd/commands" - "github.com/urfave/cli" ) func main() { - //Initialize an instance of basecoin with default basecoin commands - app := cli.NewApp() - app.Name = "example-plugin" - app.Usage = "example-plugin [command] [args...]" - app.Version = "0.1.0" - app.Commands = []cli.Command{ + + //Initialize example-plugin root command + var RootCmd = &cobra.Command{ + Use: "example-plugin", + Short: "example-plugin usage description", + } + + //Add the default basecoin commands to the root command + RootCmd.AddCommand( + commands.InitCmd, commands.StartCmd, commands.TxCmd, - commands.KeyCmd, commands.QueryCmd, + commands.KeyCmd, + commands.VerifyCmd, + commands.BlockCmd, commands.AccountCmd, + commands.UnsafeResetAllCmd, + ) + + //Run the root command + if err := RootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) } - app.Run(os.Args) } diff --git a/glide.lock b/glide.lock index 38755f1a6a..eedcffdccc 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: c023dbd97e1ea0a525e33738f03afd6be61f997f1c2592a5d9928fdcecc71361 -updated: 2017-04-21T17:38:13.194966906+02:00 +hash: 57732245af8acdb6bc7f20b36e2e0329b60570797ac47353edee108129b1b600 +updated: 2017-04-13T18:07:44.07521907-04:00 imports: - name: github.com/btcsuite/btcd version: 583684b21bfbde9b5fc4403916fd7c807feb0289 @@ -19,6 +19,8 @@ imports: version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/gorilla/websocket version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13 +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/jmhodges/levigo version: c42d9e0ca023e2198120196f842701bb4c55d7b9 - name: github.com/mattn/go-colorable @@ -26,7 +28,11 @@ imports: - name: github.com/mattn/go-isatty version: 57fdcb988a5c543893cc61bce354a6e24ab70022 - name: github.com/pkg/errors - version: bfd5150e4e41705ded2129ec33379de1cb90b513 + version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/spf13/cobra + version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c +- name: github.com/spf13/pflag + version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 - name: github.com/syndtr/goleveldb version: 3c5717caf1475fd25964109a0fc640bd150fce43 subpackages: @@ -43,7 +49,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 56e13d87f4e3ec1ea756957d6b23caa6ebcf0998 + version: af792eac777de757cd496349a5f6b5313738fcbc subpackages: - client - example/dummy @@ -59,11 +65,11 @@ imports: - name: github.com/tendermint/go-clist version: 3baa390bbaf7634251c42ad69a8682e7e3990552 - name: github.com/tendermint/go-common - version: f9e3db037330c8a8d61d3966de8473eaf01154fa + version: dcb015dff6c7af21e65c8e2f3b450df19d38c777 - name: github.com/tendermint/go-config version: 620dcbbd7d587cf3599dedbf329b64311b0c307a - name: github.com/tendermint/go-crypto - version: 0ca2c6fdb0706001ca4c4b9b80c9f428e8cf39da + version: 3f47cfac5fcd9e0f1727c7db980b3559913b3e3a - name: github.com/tendermint/go-data version: e7fcc6d081ec8518912fcdc103188275f83a3ee5 - name: github.com/tendermint/go-db @@ -83,13 +89,13 @@ imports: subpackages: - upnp - name: github.com/tendermint/go-rpc - version: 9d18cbe74e66f875afa36d2fa3be280e4a2dc9e6 + version: fcea0cda21f64889be00a0f4b6d13266b1a76ee7 subpackages: - client - server - types - name: github.com/tendermint/go-wire - version: c1c9a57ab8038448ddea1714c0698f8051e5748c + version: f530b7af7a8b06e612c2063bff6ace49060a085e - name: github.com/tendermint/log15 version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 subpackages: @@ -100,9 +106,10 @@ imports: - app - client - name: github.com/tendermint/tendermint - version: 7cf773e2d37b2b5a08bc94fb125cfd346b834824 + version: 022a5509181a19f78995a3dd9bba470333fe2d63 subpackages: - blockchain + - cmd/tendermint/commands - config/tendermint - consensus - mempool @@ -117,8 +124,6 @@ imports: - state/txindex/null - types - version -- name: github.com/urfave/cli - version: 0bdeddeeb0f650497d603c4ad7b20cfe685682f6 - name: golang.org/x/crypto version: 728b753d0135da6801d45a38e6f43ff55779c5c2 subpackages: diff --git a/glide.yaml b/glide.yaml index ecf4f151b4..420cc1ceba 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,22 +1,24 @@ package: github.com/tendermint/basecoin import: - package: github.com/tendermint/go-common - version: develop + version: master - package: github.com/tendermint/go-crypto - version: develop + version: master - package: github.com/tendermint/go-events - version: develop + version: master - package: github.com/tendermint/go-logger - version: develop + version: master +- package: github.com/tendermint/go-data + version: master - package: github.com/tendermint/go-rpc - version: develop + version: master - package: github.com/tendermint/go-wire - version: develop + version: master - package: github.com/tendermint/merkleeyes - version: develop + version: master - package: github.com/tendermint/tendermint - version: develop + version: cli_change - package: github.com/tendermint/abci - version: develop + version: master - package: github.com/gorilla/websocket version: v1.1.0