diff --git a/.circleci/config.yml b/.circleci/config.yml index 629b0e2473f2c25aa76e6da51de7e67ff17020ba..e062f42388526a2d0914409fe4ef9d98f5fb8153 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,8 +37,10 @@ jobs: - run: make test_integration_bosmarmot - store_artifacts: path: ./.gopath_bos/src/github.com/monax/bosmarmot/monax/tests/burrow.log + destination: burrow.log - store_artifacts: path: ./.gopath_bos/src/github.com/monax/bosmarmot/monax/tests/keys.log + destination: keys.log ensure_vendor: <<: *defaults diff --git a/cmd/burrow/commands/configure.go b/cmd/burrow/commands/configure.go index f1af9e0f878b70e6948a33b9a49c629b7aa08859..97c64432f120452f7db6e5c8004081e235687f7c 100644 --- a/cmd/burrow/commands/configure.go +++ b/cmd/burrow/commands/configure.go @@ -3,7 +3,6 @@ package commands import ( "fmt" "io/ioutil" - "os" "strings" "github.com/hyperledger/burrow/config" @@ -20,156 +19,158 @@ import ( "github.com/jawher/mow.cli" ) -func Configure(cmd *cli.Cmd) { - genesisSpecOpt := cmd.StringOpt("s genesis-spec", "", - "A GenesisSpec to use as a template for a GenesisDoc that will be created along with keys") +func Configure(output Output) func(cmd *cli.Cmd) { + return func(cmd *cli.Cmd) { + genesisSpecOpt := cmd.StringOpt("s genesis-spec", "", + "A GenesisSpec to use as a template for a GenesisDoc that will be created along with keys") - jsonOutOpt := cmd.BoolOpt("j json", false, "Emit config in JSON rather than TOML "+ - "suitable for further processing") + jsonOutOpt := cmd.BoolOpt("j json", false, "Emit config in JSON rather than TOML "+ + "suitable for further processing") - keysUrlOpt := cmd.StringOpt("k keys-url", "", fmt.Sprintf("Provide keys URL, default: %s", - keys.DefaultKeysConfig().URL)) + keysUrlOpt := cmd.StringOpt("k keys-url", "", fmt.Sprintf("Provide keys URL, default: %s", + keys.DefaultKeysConfig().URL)) - configOpt := cmd.StringOpt("c base-config", "", "Use the a specified burrow config file as a base") + configOpt := cmd.StringOpt("c base-config", "", "Use the a specified burrow config file as a base") - genesisDocOpt := cmd.StringOpt("g genesis-doc", "", "GenesisDoc in JSON or TOML to embed in config") + genesisDocOpt := cmd.StringOpt("g genesis-doc", "", "GenesisDoc in JSON or TOML to embed in config") - generateKeysOpt := cmd.StringOpt("x generate-keys", "", - "File to output containing secret keys as JSON or according to a custom template (see --keys-template). "+ - "Note that using this options means the keys will not be generated in the default keys instance") + generateKeysOpt := cmd.StringOpt("x generate-keys", "", + "File to output containing secret keys as JSON or according to a custom template (see --keys-template). "+ + "Note that using this options means the keys will not be generated in the default keys instance") - keysTemplateOpt := cmd.StringOpt("z keys-template", deployment.DefaultDumpKeysFormat, - fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+ - "file specified with --generate-keys.", deployment.LeftTemplateDelim, deployment.RightTemplateDelim)) + keysTemplateOpt := cmd.StringOpt("z keys-template", deployment.DefaultDumpKeysFormat, + fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+ + "file specified with --generate-keys.", deployment.LeftTemplateDelim, deployment.RightTemplateDelim)) - separateGenesisDoc := cmd.StringOpt("w separate-genesis-doc", "", "Emit a separate genesis doc as JSON or TOML") + separateGenesisDoc := cmd.StringOpt("w separate-genesis-doc", "", "Emit a separate genesis doc as JSON or TOML") - loggingOpt := cmd.StringOpt("l logging", "", - "Comma separated list of logging instructions which form a 'program' which is a depth-first "+ - "pre-order of instructions that will build the root logging sink. See 'burrow help' for more information.") + loggingOpt := cmd.StringOpt("l logging", "", + "Comma separated list of logging instructions which form a 'program' which is a depth-first "+ + "pre-order of instructions that will build the root logging sink. See 'burrow help' for more information.") - describeLoggingOpt := cmd.BoolOpt("describe-logging", false, - "Print an exhaustive list of logging instructions available with the --logging option") + describeLoggingOpt := cmd.BoolOpt("describe-logging", false, + "Print an exhaustive list of logging instructions available with the --logging option") - debugOpt := cmd.BoolOpt("d debug", false, "Include maximal debug options in config "+ - "including logging opcodes and dumping EVM tokens to disk these can be later pruned from the "+ - "generated config.") + debugOpt := cmd.BoolOpt("d debug", false, "Include maximal debug options in config "+ + "including logging opcodes and dumping EVM tokens to disk these can be later pruned from the "+ + "generated config.") - chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name") + chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name") - cmd.Spec = "[--keys-url=<keys URL> | (--generate-keys=<secret keys files> [--keys-template=<text template for each key>])] " + - "[--genesis-spec=<GenesisSpec file> | --genesis-doc=<GenesisDoc file>] " + - "[--separate-genesis-doc=<genesis JSON file>] [--chain-name] [--json] " + - "[--logging=<logging program>] [--describe-logging] [--debug]" + cmd.Spec = "[--keys-url=<keys URL> | (--generate-keys=<secret keys files> [--keys-template=<text template for each key>])] " + + "[--genesis-spec=<GenesisSpec file> | --genesis-doc=<GenesisDoc file>] " + + "[--separate-genesis-doc=<genesis JSON file>] [--chain-name] [--json] " + + "[--logging=<logging program>] [--describe-logging] [--debug]" - cmd.Action = func() { - conf := config.DefaultBurrowConfig() + cmd.Action = func() { + conf := config.DefaultBurrowConfig() - if *configOpt != "" { - // If explicitly given a config file use it as a base: - err := source.FromFile(*configOpt, conf) - if err != nil { - fatalf("could not read base config file (as TOML): %v", err) + if *configOpt != "" { + // If explicitly given a config file use it as a base: + err := source.FromFile(*configOpt, conf) + if err != nil { + output.Fatalf("could not read base config file (as TOML): %v", err) + } } - } - if *describeLoggingOpt { - fmt.Printf("Usage:\n burrow configure -l INSTRUCTION[,...]\n\nBuilds a logging " + - "configuration by constructing a tree of logging sinks assembled from preset instructions " + - "that generate the tree while traversing it.\n\nLogging Instructions:\n") - for _, instruction := range presets.Instructons() { - fmt.Printf(" %-15s\t%s\n", instruction.Name(), instruction.Description()) + if *describeLoggingOpt { + output.Logf("Usage:\n burrow configure -l INSTRUCTION[,...]\n\nBuilds a logging " + + "configuration by constructing a tree of logging sinks assembled from preset instructions " + + "that generate the tree while traversing it.\n\nLogging Instructions:\n") + for _, instruction := range presets.Instructons() { + output.Logf(" %-15s\t%s\n", instruction.Name(), instruction.Description()) + } + output.Logf("\nExample Usage:\n burrow configure -l include-any,info,stderr\n") + return } - fmt.Printf("\nExample Usage:\n burrow configure -l include-any,info,stderr\n") - return - } - - if *keysUrlOpt != "" { - conf.Keys.URL = *keysUrlOpt - } - // Genesis Spec - if *genesisSpecOpt != "" { - genesisSpec := new(spec.GenesisSpec) - err := source.FromFile(*genesisSpecOpt, genesisSpec) - if err != nil { - fatalf("Could not read GenesisSpec: %v", err) + if *keysUrlOpt != "" { + conf.Keys.URL = *keysUrlOpt } - if *generateKeysOpt != "" { - keyClient := mock.NewKeyClient() - conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient) + + // Genesis Spec + if *genesisSpecOpt != "" { + genesisSpec := new(spec.GenesisSpec) + err := source.FromFile(*genesisSpecOpt, genesisSpec) if err != nil { - fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err) + output.Fatalf("Could not read GenesisSpec: %v", err) + } + if *generateKeysOpt != "" { + keyClient := mock.NewKeyClient() + conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient) + if err != nil { + output.Fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err) + } + + pkg := deployment.Package{Keys: keyClient.Keys()} + secretKeysString, err := pkg.Dump(*keysTemplateOpt) + if err != nil { + output.Fatalf("Could not dump keys: %v", err) + } + err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700) + if err != nil { + output.Fatalf("Could not write secret keys: %v", err) + } + } else { + conf.GenesisDoc, err = genesisSpec.GenesisDoc(keys.NewKeyClient(conf.Keys.URL, logging.NewNoopLogger())) } - - pkg := deployment.Package{Keys: keyClient.Keys()} - secretKeysString, err := pkg.Dump(*keysTemplateOpt) if err != nil { - fatalf("Could not dump keys: %v", err) + output.Fatalf("could not realise GenesisSpec: %v", err) } - err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700) + } else if *genesisDocOpt != "" { + genesisDoc := new(genesis.GenesisDoc) + err := source.FromFile(*genesisSpecOpt, genesisDoc) if err != nil { - fatalf("Could not write secret keys: %v", err) + output.Fatalf("could not read GenesisSpec: %v", err) } - } else { - conf.GenesisDoc, err = genesisSpec.GenesisDoc(keys.NewKeyClient(conf.Keys.URL, logging.NewNoopLogger())) - } - if err != nil { - fatalf("could not realise GenesisSpec: %v", err) + conf.GenesisDoc = genesisDoc } - } else if *genesisDocOpt != "" { - genesisDoc := new(genesis.GenesisDoc) - err := source.FromFile(*genesisSpecOpt, genesisDoc) - if err != nil { - fatalf("could not read GenesisSpec: %v", err) - } - conf.GenesisDoc = genesisDoc - } - // Logging - if *loggingOpt != "" { - ops := strings.Split(*loggingOpt, ",") - sinkConfig, err := presets.BuildSinkConfig(ops...) - if err != nil { - fatalf("could not build logging configuration: %v\n\nTo see possible logging "+ - "instructions run:\n burrow configure --describe-logging", err) - } - conf.Logging = &logging_config.LoggingConfig{ - RootSink: sinkConfig, + // Logging + if *loggingOpt != "" { + ops := strings.Split(*loggingOpt, ",") + sinkConfig, err := presets.BuildSinkConfig(ops...) + if err != nil { + output.Fatalf("could not build logging configuration: %v\n\nTo see possible logging "+ + "instructions run:\n burrow configure --describe-logging", err) + } + conf.Logging = &logging_config.LoggingConfig{ + RootSink: sinkConfig, + } } - } - if *debugOpt { - conf.Execution = &execution.ExecutionConfig{ - VMOptions: []execution.VMOption{execution.DumpTokens, execution.DebugOpcodes}, + if *debugOpt { + conf.Execution = &execution.ExecutionConfig{ + VMOptions: []execution.VMOption{execution.DumpTokens, execution.DebugOpcodes}, + } } - } - if *chainNameOpt != "" { - if conf.GenesisDoc == nil { - fatalf("Unable to set ChainName since no GenesisDoc/GenesisSpec provided.") + if *chainNameOpt != "" { + if conf.GenesisDoc == nil { + output.Fatalf("Unable to set ChainName since no GenesisDoc/GenesisSpec provided.") + } + conf.GenesisDoc.ChainName = *chainNameOpt } - conf.GenesisDoc.ChainName = *chainNameOpt - } - if *separateGenesisDoc != "" { - if conf.GenesisDoc == nil { - fatalf("Cannot write separate genesis doc since no GenesisDoc/GenesisSpec provided.") - } - genesisDocJSON, err := conf.GenesisDoc.JSONBytes() - if err != nil { - fatalf("Could not form GenesisDoc JSON: %v", err) + if *separateGenesisDoc != "" { + if conf.GenesisDoc == nil { + output.Fatalf("Cannot write separate genesis doc since no GenesisDoc/GenesisSpec provided.") + } + genesisDocJSON, err := conf.GenesisDoc.JSONBytes() + if err != nil { + output.Fatalf("Could not form GenesisDoc JSON: %v", err) + } + err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700) + if err != nil { + output.Fatalf("Could not write GenesisDoc JSON: %v", err) + } + conf.GenesisDoc = nil } - err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700) - if err != nil { - fatalf("Could not write GenesisDoc JSON: %v", err) + if *jsonOutOpt { + output.Printf(conf.JSONString()) + } else { + output.Printf(conf.TOMLString()) } - conf.GenesisDoc = nil - } - if *jsonOutOpt { - os.Stdout.WriteString(conf.JSONString()) - } else { - os.Stdout.WriteString(conf.TOMLString()) } } } diff --git a/cmd/burrow/commands/helpers.go b/cmd/burrow/commands/helpers.go index a36ca827e80083c257a58f569c60f5f126cead58..6c2a1b211dc8b26a7d726342b9d213a74f02aa7f 100644 --- a/cmd/burrow/commands/helpers.go +++ b/cmd/burrow/commands/helpers.go @@ -2,21 +2,16 @@ package commands import ( "fmt" - "os" "github.com/hyperledger/burrow/config" "github.com/hyperledger/burrow/config/source" "github.com/hyperledger/burrow/genesis" ) -// Print informational output to Stderr -func printf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, format+"\n", args...) -} - -func fatalf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, format+"\n", args...) - os.Exit(1) +type Output interface { + Printf(format string, args ...interface{}) + Logf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) } func burrowConfigProvider(configFile string) source.ConfigProvider { diff --git a/cmd/burrow/commands/spec.go b/cmd/burrow/commands/spec.go index 6a0d3334439947e279f94703d0035c2f79f46023..36fad65a7fcd53b66bd6385bc133f7b3e927db79 100644 --- a/cmd/burrow/commands/spec.go +++ b/cmd/burrow/commands/spec.go @@ -2,66 +2,66 @@ package commands import ( "fmt" - "os" "github.com/hyperledger/burrow/config/source" "github.com/hyperledger/burrow/genesis/spec" "github.com/jawher/mow.cli" ) -func Spec(cmd *cli.Cmd) { - tomlOpt := cmd.BoolOpt("t toml", false, "Emit GenesisSpec as TOML rather than the "+ - "default JSON") +func Spec(output Output) func(cmd *cli.Cmd) { + return func(cmd *cli.Cmd) { + tomlOpt := cmd.BoolOpt("t toml", false, "Emit GenesisSpec as TOML rather than the "+ + "default JSON") - baseSpecsArg := cmd.StringsArg("BASE", nil, "Provide a base GenesisSpecs on top of which any "+ - "additional GenesisSpec presets specified by other flags will be merged. GenesisSpecs appearing "+ - "later take precedent over those appearing early if multiple --base flags are provided") + baseSpecsArg := cmd.StringsArg("BASE", nil, "Provide a base GenesisSpecs on top of which any "+ + "additional GenesisSpec presets specified by other flags will be merged. GenesisSpecs appearing "+ + "later take precedent over those appearing early if multiple --base flags are provided") - accountNamePrefixOpt := cmd.StringOpt("x name-prefix", "", "Prefix added to the names of accounts in GenesisSpec") - fullOpt := cmd.IntOpt("f full-accounts", 0, "Number of preset Full type accounts") - validatorOpt := cmd.IntOpt("v validator-accounts", 0, "Number of preset Validator type accounts") - rootOpt := cmd.IntOpt("r root-accounts", 0, "Number of preset Root type accounts") - developerOpt := cmd.IntOpt("d developer-accounts", 0, "Number of preset Developer type accounts") - participantsOpt := cmd.IntOpt("p participant-accounts", 0, "Number of preset Participant type accounts") - chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name") + accountNamePrefixOpt := cmd.StringOpt("x name-prefix", "", "Prefix added to the names of accounts in GenesisSpec") + fullOpt := cmd.IntOpt("f full-accounts", 0, "Number of preset Full type accounts") + validatorOpt := cmd.IntOpt("v validator-accounts", 0, "Number of preset Validator type accounts") + rootOpt := cmd.IntOpt("r root-accounts", 0, "Number of preset Root type accounts") + developerOpt := cmd.IntOpt("d developer-accounts", 0, "Number of preset Developer type accounts") + participantsOpt := cmd.IntOpt("p participant-accounts", 0, "Number of preset Participant type accounts") + chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name") - cmd.Spec = "[--name-prexix=<prefix for account names>][--full-accounts] [--validator-accounts] [--root-accounts] " + - "[--developer-accounts] [--participant-accounts] [--chain-name] [--toml] [BASE...]" + cmd.Spec = "[--name-prefix=<prefix for account names>][--full-accounts] [--validator-accounts] [--root-accounts] " + + "[--developer-accounts] [--participant-accounts] [--chain-name] [--toml] [BASE...]" - cmd.Action = func() { - specs := make([]spec.GenesisSpec, 0, *participantsOpt+*fullOpt) - for _, baseSpec := range *baseSpecsArg { - genesisSpec := new(spec.GenesisSpec) - err := source.FromFile(baseSpec, genesisSpec) - if err != nil { - fatalf("could not read GenesisSpec: %v", err) + cmd.Action = func() { + specs := make([]spec.GenesisSpec, 0, *participantsOpt+*fullOpt) + for _, baseSpec := range *baseSpecsArg { + genesisSpec := new(spec.GenesisSpec) + err := source.FromFile(baseSpec, genesisSpec) + if err != nil { + output.Fatalf("could not read GenesisSpec: %v", err) + } + specs = append(specs, *genesisSpec) + } + for i := 0; i < *fullOpt; i++ { + specs = append(specs, spec.FullAccount(fmt.Sprintf("%sFull_%v", *accountNamePrefixOpt, i))) + } + for i := 0; i < *validatorOpt; i++ { + specs = append(specs, spec.ValidatorAccount(fmt.Sprintf("%sValidator_%v", *accountNamePrefixOpt, i))) + } + for i := 0; i < *rootOpt; i++ { + specs = append(specs, spec.RootAccount(fmt.Sprintf("%sRoot_%v", *accountNamePrefixOpt, i))) + } + for i := 0; i < *developerOpt; i++ { + specs = append(specs, spec.DeveloperAccount(fmt.Sprintf("%sDeveloper_%v", *accountNamePrefixOpt, i))) + } + for i := 0; i < *participantsOpt; i++ { + specs = append(specs, spec.ParticipantAccount(fmt.Sprintf("%sParticipant_%v", *accountNamePrefixOpt, i))) + } + genesisSpec := spec.MergeGenesisSpecs(specs...) + if *chainNameOpt != "" { + genesisSpec.ChainName = *chainNameOpt + } + if *tomlOpt { + output.Printf(source.TOMLString(genesisSpec)) + } else { + output.Printf(source.JSONString(genesisSpec)) } - specs = append(specs, *genesisSpec) - } - for i := 0; i < *fullOpt; i++ { - specs = append(specs, spec.FullAccount(fmt.Sprintf("%sFull_%v", *accountNamePrefixOpt, i))) - } - for i := 0; i < *validatorOpt; i++ { - specs = append(specs, spec.ValidatorAccount(fmt.Sprintf("%sValidator_%v", *accountNamePrefixOpt, i))) - } - for i := 0; i < *rootOpt; i++ { - specs = append(specs, spec.RootAccount(fmt.Sprintf("%sRoot_%v", *accountNamePrefixOpt, i))) - } - for i := 0; i < *developerOpt; i++ { - specs = append(specs, spec.DeveloperAccount(fmt.Sprintf("%sDeveloper_%v", *accountNamePrefixOpt, i))) - } - for i := 0; i < *participantsOpt; i++ { - specs = append(specs, spec.ParticipantAccount(fmt.Sprintf("%sParticipant_%v", *accountNamePrefixOpt, i))) - } - genesisSpec := spec.MergeGenesisSpecs(specs...) - if *chainNameOpt != "" { - genesisSpec.ChainName = *chainNameOpt - } - if *tomlOpt { - os.Stdout.WriteString(source.TOMLString(genesisSpec)) - } else { - os.Stdout.WriteString(source.JSONString(genesisSpec)) } - os.Stdout.WriteString("\n") } } diff --git a/cmd/burrow/commands/start.go b/cmd/burrow/commands/start.go index 5efb282a6b8e8eed18b0bbe985df051fa3ecacd5..7d112ffb2f27e790826ae0ce0309a7166bb06116 100644 --- a/cmd/burrow/commands/start.go +++ b/cmd/burrow/commands/start.go @@ -10,104 +10,106 @@ import ( "github.com/jawher/mow.cli" ) -func Start(cmd *cli.Cmd) { - genesisOpt := cmd.StringOpt("g genesis", "", - "Use the specified genesis JSON file rather than a key in the main config, use - to read from STDIN") - - configOpt := cmd.StringOpt("c config", "", "Use the a specified burrow config file") - - validatorIndexOpt := cmd.Int(cli.IntOpt{ - Name: "v validator-index", - Desc: "Validator index (in validators list - GenesisSpec or GenesisDoc) from which to set ValidatorAddress", - Value: -1, - EnvVar: "BURROW_VALIDATOR_INDEX", - }) - - validatorAddressOpt := cmd.String(cli.StringOpt{ - Name: "a validator-address", - Desc: "The address of the the signing key of this validator", - EnvVar: "BURROW_VALIDATOR_ADDRESS", - }) - - validatorPassphraseOpt := cmd.String(cli.StringOpt{ - Name: "p validator-passphrase", - Desc: "The passphrase of the signing key of this validator (currently unimplemented but planned for future version of our KeyClient interface)", - EnvVar: "BURROW_VALIDATOR_PASSPHRASE", - }) - - validatorMonikerOpt := cmd.String(cli.StringOpt{ - Name: "m validator-moniker", - Desc: "An optional human-readable moniker to identify this validator amongst Tendermint peers in logs and status queries", - EnvVar: "BURROW_VALIDATOR_MONIKER", - }) - - cmd.Spec = "[--config=<config file>] [--validator-moniker=<human readable moniker>] " + - "[--validator-index=<index of validator in GenesisDoc> | --validator-address=<address of validator signing key>] " + - "[--genesis=<genesis json file>]" - - cmd.Action = func() { - - // We need to reflect on whether this obscures where values are coming from - conf := config.DefaultBurrowConfig() - // We treat logging a little differently in that if anything is set for logging we will not - // set default outputs - conf.Logging = nil - err := source.EachOf( - burrowConfigProvider(*configOpt), - source.FirstOf( - genesisDocProvider(*genesisOpt, false), - // Try working directory - genesisDocProvider(config.DefaultGenesisDocJSONFileName, true)), - ).Apply(conf) - - // If no logging config was provided use the default - if conf.Logging == nil { - conf.Logging = logging_config.DefaultNodeLoggingConfig() - } - if err != nil { - fatalf("could not obtain config: %v", err) - } - - // Which validator am I? - if *validatorAddressOpt != "" { - address, err := acm.AddressFromHexString(*validatorAddressOpt) - if err != nil { - fatalf("could not read address for validator in '%s'", *validatorAddressOpt) +func Start(output Output) func(cmd *cli.Cmd) { + return func(cmd *cli.Cmd) { + genesisOpt := cmd.StringOpt("g genesis", "", + "Use the specified genesis JSON file rather than a key in the main config, use - to read from STDIN") + + configOpt := cmd.StringOpt("c config", "", "Use the a specified burrow config file") + + validatorIndexOpt := cmd.Int(cli.IntOpt{ + Name: "v validator-index", + Desc: "Validator index (in validators list - GenesisSpec or GenesisDoc) from which to set ValidatorAddress", + Value: -1, + EnvVar: "BURROW_VALIDATOR_INDEX", + }) + + validatorAddressOpt := cmd.String(cli.StringOpt{ + Name: "a validator-address", + Desc: "The address of the the signing key of this validator", + EnvVar: "BURROW_VALIDATOR_ADDRESS", + }) + + validatorPassphraseOpt := cmd.String(cli.StringOpt{ + Name: "p validator-passphrase", + Desc: "The passphrase of the signing key of this validator (currently unimplemented but planned for future version of our KeyClient interface)", + EnvVar: "BURROW_VALIDATOR_PASSPHRASE", + }) + + validatorMonikerOpt := cmd.String(cli.StringOpt{ + Name: "m validator-moniker", + Desc: "An optional human-readable moniker to identify this validator amongst Tendermint peers in logs and status queries", + EnvVar: "BURROW_VALIDATOR_MONIKER", + }) + + cmd.Spec = "[--config=<config file>] [--validator-moniker=<human readable moniker>] " + + "[--validator-index=<index of validator in GenesisDoc> | --validator-address=<address of validator signing key>] " + + "[--genesis=<genesis json file>]" + + cmd.Action = func() { + + // We need to reflect on whether this obscures where values are coming from + conf := config.DefaultBurrowConfig() + // We treat logging a little differently in that if anything is set for logging we will not + // set default outputs + conf.Logging = nil + err := source.EachOf( + burrowConfigProvider(*configOpt), + source.FirstOf( + genesisDocProvider(*genesisOpt, false), + // Try working directory + genesisDocProvider(config.DefaultGenesisDocJSONFileName, true)), + ).Apply(conf) + + // If no logging config was provided use the default + if conf.Logging == nil { + conf.Logging = logging_config.DefaultNodeLoggingConfig() } - conf.ValidatorAddress = &address - } else if *validatorIndexOpt > -1 { - if conf.GenesisDoc == nil { - fatalf("Unable to set ValidatorAddress from provided validator-index since no " + - "GenesisDoc/GenesisSpec provided.") + if err != nil { + output.Fatalf("could not obtain config: %v", err) } - if *validatorIndexOpt >= len(conf.GenesisDoc.Validators) { - fatalf("validator-index of %v given but only %v validators specified in GenesisDoc", - *validatorIndexOpt, len(conf.GenesisDoc.Validators)) + + // Which validator am I? + if *validatorAddressOpt != "" { + address, err := acm.AddressFromHexString(*validatorAddressOpt) + if err != nil { + output.Fatalf("could not read address for validator in '%s'", *validatorAddressOpt) + } + conf.ValidatorAddress = &address + } else if *validatorIndexOpt > -1 { + if conf.GenesisDoc == nil { + output.Fatalf("Unable to set ValidatorAddress from provided validator-index since no " + + "GenesisDoc/GenesisSpec provided.") + } + if *validatorIndexOpt >= len(conf.GenesisDoc.Validators) { + output.Fatalf("validator-index of %v given but only %v validators specified in GenesisDoc", + *validatorIndexOpt, len(conf.GenesisDoc.Validators)) + } + conf.ValidatorAddress = &conf.GenesisDoc.Validators[*validatorIndexOpt].Address + output.Logf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress) } - conf.ValidatorAddress = &conf.GenesisDoc.Validators[*validatorIndexOpt].Address - printf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress) - } - if *validatorPassphraseOpt != "" { - conf.ValidatorPassphrase = validatorPassphraseOpt - } + if *validatorPassphraseOpt != "" { + conf.ValidatorPassphrase = validatorPassphraseOpt + } - if *validatorMonikerOpt != "" { - conf.Tendermint.Moniker = *validatorMonikerOpt - } + if *validatorMonikerOpt != "" { + conf.Tendermint.Moniker = *validatorMonikerOpt + } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - kern, err := conf.Kernel(ctx) - if err != nil { - fatalf("could not create Burrow kernel: %v", err) - } + kern, err := conf.Kernel(ctx) + if err != nil { + output.Fatalf("could not create Burrow kernel: %v", err) + } - err = kern.Boot() - if err != nil { - fatalf("could not boot Burrow kernel: %v", err) + err = kern.Boot() + if err != nil { + output.Fatalf("could not boot Burrow kernel: %v", err) + } + kern.WaitForShutdown() } - kern.WaitForShutdown() } } diff --git a/cmd/burrow/main.go b/cmd/burrow/main.go index 6c57758674250090e19c3d0f386248942bf10cb4..cddf19edc8d799e63f726b197ee34357adb0a7df 100644 --- a/cmd/burrow/main.go +++ b/cmd/burrow/main.go @@ -10,10 +10,11 @@ import ( ) func main() { - burrow().Run(os.Args) + // Print informational output to Stderr + burrow(stdOutput()).Run(os.Args) } -func burrow() *cli.Cli { +func burrow(output commands.Output) *cli.Cli { app := cli.App("burrow", "The EVM smart contract machine with Tendermint consensus") versionOpt := app.BoolOpt("v version", false, "Print the Burrow version") @@ -22,19 +23,51 @@ func burrow() *cli.Cli { app.Action = func() { if *versionOpt { fmt.Println(project.FullVersion()) - os.Exit(0) } } app.Command("start", "Start a Burrow node", - commands.Start) + commands.Start(output)) app.Command("spec", "Build a GenesisSpec that acts as a template for a GenesisDoc and the configure command", - commands.Spec) + commands.Spec(output)) app.Command("configure", "Create Burrow configuration by consuming a GenesisDoc or GenesisSpec, creating keys, and emitting the config", - commands.Configure) + commands.Configure(output)) return app } + +func stdOutput() *output { + return &output{ + PrintfFunc: func(format string, args ...interface{}) { + fmt.Fprintf(os.Stdout, format+"\n", args...) + }, + LogfFunc: func(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format+"\n", args...) + }, + FatalfFunc: func(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format+"\n", args...) + os.Exit(1) + }, + } +} + +type output struct { + PrintfFunc func(format string, args ...interface{}) + LogfFunc func(format string, args ...interface{}) + FatalfFunc func(format string, args ...interface{}) +} + +func (out *output) Printf(format string, args ...interface{}) { + out.PrintfFunc(format, args...) +} + +func (out *output) Logf(format string, args ...interface{}) { + out.LogfFunc(format, args...) +} + +func (out *output) Fatalf(format string, args ...interface{}) { + out.FatalfFunc(format, args...) +} diff --git a/cmd/burrow/main_test.go b/cmd/burrow/main_test.go index c2690fd223643ab69953e63311694b2b99764e67..7a9b5c13e18ec2531d29f99ce4606498d091d3c3 100644 --- a/cmd/burrow/main_test.go +++ b/cmd/burrow/main_test.go @@ -1,12 +1,30 @@ package main -import "testing" +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) func TestBurrow(t *testing.T) { - app := burrow() + var outputCount int + out := &output{ + PrintfFunc: func(format string, args ...interface{}) { + outputCount++ + }, + LogfFunc: func(format string, args ...interface{}) { + outputCount++ + }, + FatalfFunc: func(format string, args ...interface{}) { + t.Fatalf("fatalf called by burrow cmd: %s", fmt.Sprintf(format, args...)) + }, + } + app := burrow(out) // Basic smoke test for cli config - app.Run([]string{"--version"}) - app.Run([]string{"spec"}) - app.Run([]string{"configure"}) - app.Run([]string{"serve"}) + assert.NoError(t, app.Run([]string{"burrow", "--version"})) + assert.NoError(t, app.Run([]string{"burrow", "spec", "--name-prefix", "foo", "-f1"})) + assert.NoError(t, app.Run([]string{"burrow", "configure"})) + assert.NoError(t, app.Run([]string{"burrow", "start", "-h"})) + assert.True(t, outputCount > 0) }