diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index 63cfd45ddb..2f81828dbf 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -1,7 +1,8 @@ package main import ( - "errors" + "encoding/json" + "fmt" "os" "github.com/spf13/cobra" @@ -21,17 +22,28 @@ var ( } ) -// TODO: move into server -var ( - initNodeCmd = &cobra.Command{ - Use: "init ", - Short: "Initialize full node", - RunE: todoNotImplemented, +// 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) -func todoNotImplemented(_ *cobra.Command, _ []string) error { - return errors.New("TODO: Command not yet implemented") + opts := fmt.Sprintf(`{ + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "mycoin", + "amount": 9007199254740992 + } + ] + }] + }`, addr) + return json.RawMessage(opts), nil } func main() { @@ -39,8 +51,8 @@ func main() { var app *baseapp.BaseApp gaiadCmd.AddCommand( - initNodeCmd, - server.StartNodeCmd(app), + server.InitCmd(defaultOptions), + server.StartCmd(app), server.UnsafeResetAllCmd(app.Logger), version.VersionCmd, ) diff --git a/server/init.go b/server/init.go new file mode 100644 index 0000000000..374e3cb3fe --- /dev/null +++ b/server/init.go @@ -0,0 +1,145 @@ +package server + +import ( + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/spf13/cobra" + + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/words" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + tmtypes "github.com/tendermint/tendermint/types" +) + +// InitCmd will initialize all files for tendermint, +// along with proper app_options. +// The application can pass in a function to generate +// proper options. And may want to use GenerateCoinKey +// to create default account(s). +func InitCmd(gen GenOptions) *cobra.Command { + cmd := initCmd{ + gen: gen, + } + return &cobra.Command{ + Use: "init", + Short: "Initialize genesis files", + RunE: cmd.run, + } +} + +// GenOptions can parse command-line and flag to +// generate default app_options for the genesis file. +// This is application-specific +type GenOptions func(args []string) (json.RawMessage, error) + +// GenerateCoinKey returns the address of a public key, +// along with the secret phrase to recover the private key. +// You can give coins to this address and return the recovery +// phrase to the user to access them. +func GenerateCoinKey() (crypto.Address, string, error) { + // construct an in-memory key store + codec, err := words.LoadCodec("english") + if err != nil { + return nil, "", err + } + keybase := keys.New( + dbm.NewMemDB(), + codec, + ) + + // generate a private key, with recovery phrase + info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519) + if err != nil { + return nil, "", err + } + addr := info.PubKey.Address() + return addr, secret, nil +} + +type initCmd struct { + gen GenOptions +} + +func (c initCmd) run(cmd *cobra.Command, args []string) error { + // Run the basic tendermint initialization, + // set up a default genesis with no app_options + cfg, err := tcmd.ParseConfig() + if err != nil { + return err + } + tcmd.InitFilesCmd.Run(cmd, args) + + // no app_options, leave like tendermint + if c.gen == nil { + return nil + } + + // Now, we want to add the custom app_options + options, err := c.gen(args) + if err != nil { + return err + } + + // And add them to the genesis file + genFile := cfg.GenesisFile() + return addGenesisOptions(genFile, options) +} + +func addGenesisOptions(filename string, options json.RawMessage) error { + bz, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + var doc tmtypes.GenesisDoc + err = json.Unmarshal(bz, &doc) + if err != nil { + return err + } + + doc.AppOptions = options + out, err := json.MarshalIndent(doc, "", " ") + if err != nil { + return err + } + + return ioutil.WriteFile(filename, out, 0600) +} + +// GetGenesisJSON returns a new tendermint genesis with Basecoin app_options +// that grant a large amount of "mycoin" to a single address +// TODO: A better UX for generating genesis files +func GetGenesisJSON(pubkey, chainID, denom, addr string, options string) string { + return fmt.Sprintf(`{ + "app_hash": "", + "chain_id": "%s", + "genesis_time": "0001-01-01T00:00:00.000Z", + "validators": [ + { + "power": 10, + "name": "", + "pub_key": { + "type": "ed25519", + "data": "%s" + } + } + ], + "app_options": { + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "%s", + "amount": 9007199254740992 + } + ] + }], + "plugin_options": [ + "coin/issuer", {"app": "sigs", "addr": "%s"}%s + ] + } +}`, chainID, pubkey, addr, denom, addr, options) +} diff --git a/server/start.go b/server/start.go index 137a0b6cc6..d369ff0167 100644 --- a/server/start.go +++ b/server/start.go @@ -21,10 +21,10 @@ const ( flagAddress = "address" ) -// StartNodeCmd runs the service passed in, either +// StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint -func StartNodeCmd(app *baseapp.BaseApp) *cobra.Command { - start := startNodeCmd{ +func StartCmd(app *baseapp.BaseApp) *cobra.Command { + start := startCmd{ app: app, } cmd := &cobra.Command{ @@ -42,14 +42,14 @@ func StartNodeCmd(app *baseapp.BaseApp) *cobra.Command { return cmd } -type startNodeCmd struct { +type startCmd struct { // do this in main: // rootDir := viper.GetString(cli.HomeFlag) // node.Logger = .... app *baseapp.BaseApp } -func (s startNodeCmd) run(cmd *cobra.Command, args []string) error { +func (s startCmd) run(cmd *cobra.Command, args []string) error { logger := s.app.Logger if !viper.GetBool(flagWithTendermint) { logger.Info("Starting ABCI without Tendermint") @@ -59,7 +59,7 @@ func (s startNodeCmd) run(cmd *cobra.Command, args []string) error { return s.startInProcess() } -func (s startNodeCmd) startStandAlone() error { +func (s startCmd) startStandAlone() error { logger := s.app.Logger // Start the ABCI listener addr := viper.GetString(flagAddress) @@ -78,7 +78,7 @@ func (s startNodeCmd) startStandAlone() error { return nil } -func (s startNodeCmd) startInProcess() error { +func (s startCmd) startInProcess() error { logger := s.app.Logger cfg, err := tcmd.ParseConfig() if err != nil {