diff --git a/examples/gaia/README.md b/examples/gaia/README.md new file mode 100644 index 000000000..485af236f --- /dev/null +++ b/examples/gaia/README.md @@ -0,0 +1,3 @@ +Gaiad is the abci application, which can be run stand-alone, or in-process with tendermint. + +Gaiacli is a client application, which connects to tendermint rpc, and sends transactions and queries the state. It uses light-client proofs to guarantee the results even if it doesn't have 100% trust in the node it connects to. diff --git a/examples/gaia/gaiacli/client.go b/examples/gaia/gaiacli/client.go new file mode 100644 index 000000000..682a571e6 --- /dev/null +++ b/examples/gaia/gaiacli/client.go @@ -0,0 +1,131 @@ +package main + +import "github.com/spf13/cobra" + +const ( + // these are needed for every init + flagChainID = "chain-id" + flagNode = "node" + + // one of the following should be provided to verify the connection + flagGenesis = "genesis" + flagCommit = "commit" + flagValHash = "validator-set" + + flagSelect = "select" + flagTags = "tag" + flagAny = "any" + + flagBind = "bind" + flagCORS = "cors" + flagTrustNode = "trust-node" + + // this is for signing + flagName = "name" +) + +var ( + statusCmd = &cobra.Command{ + Use: "status", + Short: "Query remote node for status", + RunE: todoNotImplemented, + } +) + +// AddClientCommands returns a sub-tree of all basic client commands +// +// Call AddGetCommand and AddPostCommand to add custom txs and queries +func AddClientCommands(cmd *cobra.Command) { + cmd.AddCommand( + initClientCommand(), + statusCmd, + blockCommand(), + validatorCommand(), + lineBreak, + txSearchCommand(), + txCommand(), + lineBreak, + ) +} + +// GetCommands adds common flags to query commands +func GetCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + c.Flags().Bool(flagTrustNode, false, "Don't verify proofs for responses") + } + return cmds +} + +// PostCommands adds common flags for commands to post tx +func PostCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + c.Flags().String(flagName, "", "Name of private key with which to sign") + c.Flags().String(flagPassword, "", "Password to use the named private key") + } + return cmds +} + +func initClientCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initialize light client", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagChainID, "c", "", "ID of chain we connect to") + cmd.Flags().StringP(flagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") + cmd.Flags().String(flagCommit, "", "File with trusted and signed header") + cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") + return cmd +} + +func blockCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "block ", + Short: "Get verified data for a the block at given height", + RunE: todoNotImplemented, + } + cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") + return cmd +} + +func validatorCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "validatorset ", + Short: "Get the full validator set at given height", + RunE: todoNotImplemented, + } + return cmd +} + +func serveCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "serve", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: todoNotImplemented, + } + // TODO: handle unix sockets also? + cmd.Flags().StringP(flagBind, "b", "localhost:1317", "Interface and port that server binds to") + cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") + return cmd +} + +func txSearchCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "txs", + Short: "Search for all transactions that match the given tags", + RunE: todoNotImplemented, + } + cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") + return cmd +} + +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx ", + Short: "Matches this txhash over all committed blocks", + RunE: todoNotImplemented, + } + return cmd +} diff --git a/examples/gaia/gaiacli/key.go b/examples/gaia/gaiacli/key.go new file mode 100644 index 000000000..b3ffa323a --- /dev/null +++ b/examples/gaia/gaiacli/key.go @@ -0,0 +1,77 @@ +package main + +import "github.com/spf13/cobra" + +const ( + flagPassword = "password" + flagNewPassword = "new-password" + flagType = "type" + flagSeed = "seed" + flagDryRun = "dry-run" +) + +var ( + listKeysCmd = &cobra.Command{ + Use: "list", + Short: "List all locally availably keys", + RunE: todoNotImplemented, + } + + showKeysCmd = &cobra.Command{ + Use: "show ", + Short: "Show key info for the given name", + RunE: todoNotImplemented, + } +) + +// KeyCommands registers a sub-tree of commands to interact with +// local private key storage. +func KeyCommands() *cobra.Command { + cmd := &cobra.Command{ + Use: "keys", + Short: "Add or view local private keys", + } + cmd.AddCommand( + addKeyCommand(), + listKeysCmd, + showKeysCmd, + lineBreak, + deleteKeyCommand(), + updateKeyCommand(), + ) + return cmd +} + +func addKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "add ", + Short: "Create a new key, or import from seed", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Password to encrypt private key") + cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") + cmd.Flags().StringP(flagSeed, "s", "", "Provide seed phrase to recover existing key instead of creating") + cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") + return cmd +} + +func updateKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "update ", + Short: "Change the password used to protect private key", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Current password to decrypt key") + cmd.Flags().String(flagNewPassword, "", "New password to use to protect key") + return cmd +} + +func deleteKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete the given key", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Password of existing key to delete") + return cmd +} diff --git a/examples/gaia/gaiacli/main.go b/examples/gaia/gaiacli/main.go new file mode 100644 index 000000000..dce125acb --- /dev/null +++ b/examples/gaia/gaiacli/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "errors" + "os" + + "github.com/spf13/cobra" + + "github.com/tendermint/tmlibs/cli" + + "github.com/cosmos/cosmos-sdk/version" +) + +const ( + flagTo = "to" + flagAmount = "amount" + flagFee = "fee" +) + +// gaiacliCmd is the entry point for this binary +var ( + gaiacliCmd = &cobra.Command{ + Use: "gaiacli", + Short: "Gaia light-client", + } + + lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} + + getAccountCmd = &cobra.Command{ + Use: "account
", + Short: "Query account balance", + RunE: todoNotImplemented, + } +) + +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} + +func postSendCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "send", + Short: "Create and sign a send tx", + RunE: todoNotImplemented, + } + cmd.Flags().String(flagTo, "", "Address to send coins") + cmd.Flags().String(flagAmount, "", "Amount of coins to send") + cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") + return cmd +} + +func main() { + // disable sorting + cobra.EnableCommandSorting = false + + // generic client commands + AddClientCommands(gaiacliCmd) + // query commands (custom to binary) + gaiacliCmd.AddCommand( + GetCommands(getAccountCmd)...) + // post tx commands (custom to binary) + gaiacliCmd.AddCommand( + PostCommands(postSendCommand())...) + + // add proxy, version and key info + gaiacliCmd.AddCommand( + lineBreak, + serveCommand(), + KeyCommands(), + lineBreak, + version.VersionCmd, + ) + + // prepare and add flags + executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli")) + executor.Execute() +} diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go new file mode 100644 index 000000000..0c4c49eec --- /dev/null +++ b/examples/gaia/gaiad/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + + abci "github.com/tendermint/abci/types" + "github.com/tendermint/tmlibs/cli" + "github.com/tendermint/tmlibs/log" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/version" +) + +// gaiadCmd is the entry point for this binary +var ( + gaiadCmd = &cobra.Command{ + Use: "gaiad", + Short: "Gaia Daemon (server)", + } +) + +// defaultOptions sets up the app_options for the +// default genesis file +func defaultOptions(args []string) (json.RawMessage, error) { + addr, secret, err := server.GenerateCoinKey() + if err != nil { + return nil, err + } + fmt.Println("Secret phrase to access coins:") + fmt.Println(secret) + + opts := fmt.Sprintf(`{ + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "mycoin", + "amount": 9007199254740992 + } + ] + }] + }`, addr) + return json.RawMessage(opts), nil +} + +func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { + // TODO: set this to something real + app := new(baseapp.BaseApp) + return app, nil +} + +func main() { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "main") + + gaiadCmd.AddCommand( + server.InitCmd(defaultOptions, logger), + server.StartCmd(generateApp, logger), + server.UnsafeResetAllCmd(logger), + version.VersionCmd, + ) + + // prepare and add flags + executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad")) + executor.Execute() +}