From 665b39e3309b230030dff570c0df7c6d496cc2b6 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 28 Jan 2017 21:12:58 -0500 Subject: [PATCH] new cli --- cmd/basecoin/main.go | 195 ++++++++++++++++++++++++++++++++---------- cmd/basecoin/start.go | 54 ++++++++++++ cmd/basecoin/tx.go | 101 ++++++++++++++++++++++ 3 files changed, 305 insertions(+), 45 deletions(-) create mode 100644 cmd/basecoin/start.go create mode 100644 cmd/basecoin/tx.go diff --git a/cmd/basecoin/main.go b/cmd/basecoin/main.go index 5dc536b91..dc940a370 100644 --- a/cmd/basecoin/main.go +++ b/cmd/basecoin/main.go @@ -1,55 +1,160 @@ package main import ( - "flag" + "os" - "github.com/tendermint/abci/server" - "github.com/tendermint/basecoin/app" - cmn "github.com/tendermint/go-common" - eyes "github.com/tendermint/merkleeyes/client" + "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", + } + + eyesDBFlag = cli.StringFlag{ + Name: "eyes-db", + Value: "merkleeyes.db", + Usage: "MerkleEyes db name for embedded", + } + + // TODO: move to config file + // eyesCacheSizePtr := flag.Int("eyes-cache-size", 10000, "MerkleEyes db cache size, for embedded") + + genesisFlag = cli.StringFlag{ + Name: "genesis", + Value: "", + Usage: "Path to genesis file, if it exists", + } + + inProcTMFlag = cli.BoolFlag{ + Name: "in-proc", + Usage: "Run Tendermint in-process with the App", + } +) + +// tx flags + +var ( + toFlag = cli.StringFlag{ + Name: "to", + Value: "", + Usage: "Destination address for the transaction", + } + + amountFlag = cli.IntFlag{ + Name: "amount", + Value: 0, + Usage: "Amount of coins to send in the transaction", + } + + fromFlag = cli.StringFlag{ + Name: "from", + Value: "priv_validator.json", + Usage: "Path to a private key to sign the transaction", + } + + seqFlag = cli.IntFlag{ + Name: "sequence", + Value: 0, + Usage: "Sequence number for the account", + } + + coinFlag = cli.StringFlag{ + Name: "coin", + Value: "blank", + Usage: "Specify a coin denomination", + } + + gasFlag = cli.IntFlag{ + Name: "gas", + Value: 0, + Usage: "The amount of gas for the transaction", + } + + feeFlag = cli.IntFlag{ + Name: "fee", + Value: 0, + Usage: "The transaction fee", + } + + 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", + } ) func main() { - addrPtr := flag.String("address", "tcp://0.0.0.0:46658", "Listen address") - eyesPtr := flag.String("eyes", "local", "MerkleEyes address, or 'local' for embedded") - eyesDBNamePtr := flag.String("eyes-db-name", "local.db", "MerkleEyes db name, for embedded") - eyesCacheSizePtr := flag.Int("eyes-cache-size", 10000, "MerkleEyes db cache size, for embedded") - genFilePath := flag.String("genesis", "", "Genesis file, if any") - flag.Parse() + app := cli.NewApp() + app.Name = "basecoin" + app.Usage = "basecoin [command] [args...]" + app.Version = "0.1.0" + app.Commands = []cli.Command{ + { + Name: "start", + Usage: "Start basecoin", + ArgsUsage: "", + Action: func(c *cli.Context) error { + return cmdStart(c) + }, + Flags: []cli.Flag{ + addrFlag, + eyesFlag, + eyesDBFlag, + genesisFlag, + inProcTMFlag, + }, + }, - // Connect to MerkleEyes - var eyesCli *eyes.Client - if *eyesPtr == "local" { - eyesCli = eyes.NewLocalClient(*eyesDBNamePtr, *eyesCacheSizePtr) - } else { - var err error - eyesCli, err = eyes.NewClient(*eyesPtr) - if err != nil { - cmn.Exit("connect to MerkleEyes: " + err.Error()) - } + { + Name: "sendtx", + Usage: "Broadcast a basecoin SendTx", + ArgsUsage: "", + Action: func(c *cli.Context) error { + return cmdSendTx(c) + }, + Flags: []cli.Flag{ + toFlag, + fromFlag, + amountFlag, + coinFlag, + gasFlag, + feeFlag, + }, + }, + + { + Name: "apptx", + Usage: "Broadcast a basecoin AppTx", + ArgsUsage: "", + Action: func(c *cli.Context) error { + return cmdAppTx(c) + }, + Flags: []cli.Flag{ + nameFlag, + fromFlag, + amountFlag, + coinFlag, + gasFlag, + feeFlag, + dataFlag, + }, + }, } - - // Create Basecoin app - app := app.NewBasecoin(eyesCli) - - // If genesis file was specified, set key-value options - if *genFilePath != "" { - err := app.LoadGenesis(*genFilePath) - if err != nil { - cmn.Exit(cmn.Fmt("%+v", err)) - } - } - - // Start the listener - svr, err := server.NewServer(*addrPtr, "socket", app) - if err != nil { - cmn.Exit("create listener: " + err.Error()) - } - - // Wait forever - cmn.TrapSignal(func() { - // Cleanup - svr.Stop() - }) - + app.Run(os.Args) } diff --git a/cmd/basecoin/start.go b/cmd/basecoin/start.go new file mode 100644 index 000000000..fde62e3d6 --- /dev/null +++ b/cmd/basecoin/start.go @@ -0,0 +1,54 @@ +package main + +import ( + "errors" + + "github.com/urfave/cli" + + "github.com/tendermint/abci/server" + "github.com/tendermint/basecoin/app" + cmn "github.com/tendermint/go-common" + eyes "github.com/tendermint/merkleeyes/client" +) + +const EyesCacheSize = 10000 + +func cmdStart(c *cli.Context) error { + + // Connect to MerkleEyes + var eyesCli *eyes.Client + if c.String("eyes") == "local" { + eyesCli = eyes.NewLocalClient(c.String("eyes-db"), EyesCacheSize) + } else { + var err error + eyesCli, err = eyes.NewClient(c.String("eyes")) + if err != nil { + return errors.New("connect to MerkleEyes: " + err.Error()) + } + } + + // Create Basecoin app + app := app.NewBasecoin(eyesCli) + + // If genesis file was specified, set key-value options + if c.String("genesis") != "" { + err := app.LoadGenesis(c.String("genesis")) + if err != nil { + return errors.New(cmn.Fmt("%+v", err)) + } + } + + // Start the listener + svr, err := server.NewServer(c.String("address"), "socket", app) + if err != nil { + return errors.New("create listener: " + err.Error()) + } + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + svr.Stop() + }) + + return nil +} diff --git a/cmd/basecoin/tx.go b/cmd/basecoin/tx.go new file mode 100644 index 000000000..3f902963a --- /dev/null +++ b/cmd/basecoin/tx.go @@ -0,0 +1,101 @@ +package main + +import ( + "encoding/hex" + "errors" + "fmt" + + "github.com/urfave/cli" + + "github.com/tendermint/basecoin/types" + cmn "github.com/tendermint/go-common" + "github.com/tendermint/go-wire" + tmtypes "github.com/tendermint/tendermint/types" +) + +func cmdSendTx(c *cli.Context) error { + toHex := c.String("to") + fromFile := c.String("from") + amount := int64(c.Int("amount")) + coin := c.String("coin") + gas, fee := c.Int("gas"), int64(c.Int("fee")) + chainID := c.String("chain_id") + + to, err := hex.DecodeString(toHex) + if err != nil { + return errors.New("To address is invalid hex: " + err.Error()) + } + + privVal := tmtypes.LoadPrivValidator(fromFile) + + sequence := getSeq(c) + + input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence) + output := newOutput(to, coin, amount) + + tx := types.SendTx{ + Gas: int64(gas), + Fee: types.Coin{coin, fee}, + Inputs: []types.TxInput{input}, + Outputs: []types.TxOutput{output}, + } + + tx.Inputs[0].Signature = privVal.Sign(tx.SignBytes(chainID)) + fmt.Println(string(wire.JSONBytes(tx))) + + return nil +} + +func cmdAppTx(c *cli.Context) error { + name := c.String("name") + fromFile := c.String("from") + amount := int64(c.Int("amount")) + coin := c.String("coin") + gas, fee := c.Int("gas"), int64(c.Int("fee")) + chainID := c.String("chain_id") + dataString := c.String("data") + + data := []byte(dataString) + if cmn.IsHex(dataString) { + data, _ = hex.DecodeString(dataString) + } + + privVal := tmtypes.LoadPrivValidator(fromFile) + + sequence := getSeq(c) + + input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence) + + tx := types.AppTx{ + Gas: int64(gas), + Fee: types.Coin{coin, fee}, + Name: name, + Input: input, + Data: data, + } + + tx.Input.Signature = privVal.Sign(tx.SignBytes(chainID)) + fmt.Println(string(wire.JSONBytes(tx))) + return nil +} + +func getSeq(c *cli.Context) int { + if c.IsSet("sequence") { + return c.Int("sequence") + } + // TODO: get from query + return 0 +} + +func newOutput(to []byte, coin string, amount int64) types.TxOutput { + return types.TxOutput{ + Address: to, + Coins: types.Coins{ + types.Coin{ + Denom: coin, + Amount: amount, + }, + }, + } + +}