diff --git a/Gopkg.lock b/Gopkg.lock index afab6930cff90332d0d79f55adc8f24ef6a55064..4267290a7b3c05a706ffc813139ff873f0327cde 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -349,6 +349,22 @@ revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab" version = "v1.1.1" +[[projects]] + name = "github.com/wayn3h0/go-uuid" + packages = [ + ".", + "internal/dcesecurity", + "internal/layout", + "internal/namebased", + "internal/namebased/md5", + "internal/namebased/sha1", + "internal/random", + "internal/timebased", + "internal/version" + ] + revision = "1622016a49b50139b1ac263e6ef2804226b3dec6" + version = "v2.2.1" + [[projects]] branch = "master" name = "golang.org/x/crypto" @@ -458,6 +474,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "0618ffbeb3a7333a1d9e7b24f256ac5b7719f25957d07cfd935156b355b2e5e2" + inputs-digest = "ecec019d5ac7140b4607333907f5a4b211fb3551f7cfe51b92e6cc8e3c515488" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/burrow/commands/configure.go b/cmd/burrow/commands/configure.go new file mode 100644 index 0000000000000000000000000000000000000000..de13421a319bce30a6291396e7fe44dfd4971c50 --- /dev/null +++ b/cmd/burrow/commands/configure.go @@ -0,0 +1,173 @@ +package commands + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/hyperledger/burrow/config" + "github.com/hyperledger/burrow/config/source" + "github.com/hyperledger/burrow/execution" + "github.com/hyperledger/burrow/genesis" + "github.com/hyperledger/burrow/genesis/spec" + "github.com/hyperledger/burrow/keys" + "github.com/hyperledger/burrow/keys/mock" + "github.com/hyperledger/burrow/logging" + logging_config "github.com/hyperledger/burrow/logging/config" + "github.com/hyperledger/burrow/logging/config/presets" + "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") + + 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)) + + 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") + + 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", mock.DefaultDumpKeysFormat, + fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+ + "file specified with --generate-keys.", mock.LeftTemplateDelim, mock.RightTemplateDelim)) + + 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.") + + 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.") + + 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.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 *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()) + } + 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 *generateKeysOpt != "" { + keyClient := mock.NewMockKeyClient() + conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient) + if err != nil { + fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err) + } + + secretKeysString, err := keyClient.DumpKeys(*keysTemplateOpt) + if err != nil { + fatalf("Could not dump keys: %v", err) + } + err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700) + if err != nil { + fatalf("Could not write secret keys: %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) + } + } 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, + } + } + + 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.") + } + 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) + } + err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700) + if err != nil { + fatalf("Could not write GenesisDoc JSON: %v", err) + } + 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 new file mode 100644 index 0000000000000000000000000000000000000000..232c1f2eb33229e03cf56c43972d5f5c86963d91 --- /dev/null +++ b/cmd/burrow/commands/helpers.go @@ -0,0 +1,52 @@ +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) +} + +func burrowConfigProvider(configFile string) source.ConfigProvider { + return source.FirstOf( + // Will fail if file doesn't exist, but still skipped it configFile == "" + source.File(configFile, false), + source.Environment(config.DefaultBurrowConfigJSONEnvironmentVariable), + // Try working directory + source.File(config.DefaultBurrowConfigTOMLFileName, true), + source.Default(config.DefaultBurrowConfig())) +} + +func genesisDocProvider(genesisFile string, skipNonExistent bool) source.ConfigProvider { + return source.NewConfigProvider(fmt.Sprintf("genesis file at %s", genesisFile), + source.ShouldSkipFile(genesisFile, skipNonExistent), + func(baseConfig interface{}) error { + conf, ok := baseConfig.(*config.BurrowConfig) + if !ok { + return fmt.Errorf("config passed was not BurrowConfig") + } + if conf.GenesisDoc != nil { + return fmt.Errorf("sourcing GenesisDoc from file %v, but GenesisDoc was defined in earlier "+ + "config source, only specify GenesisDoc in one place", genesisFile) + } + genesisDoc := new(genesis.GenesisDoc) + err := source.FromFile(genesisFile, genesisDoc) + if err != nil { + return err + } + conf.GenesisDoc = genesisDoc + return nil + }) +} diff --git a/cmd/burrow/commands/spec.go b/cmd/burrow/commands/spec.go new file mode 100644 index 0000000000000000000000000000000000000000..634006311b0a54082ed4266f7edfb2baed406b5d --- /dev/null +++ b/cmd/burrow/commands/spec.go @@ -0,0 +1,67 @@ +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") + + 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("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 = "[--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) + } + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5efb282a6b8e8eed18b0bbe985df051fa3ecacd5 --- /dev/null +++ b/cmd/burrow/commands/start.go @@ -0,0 +1,113 @@ +package commands + +import ( + "context" + + acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/config" + "github.com/hyperledger/burrow/config/source" + logging_config "github.com/hyperledger/burrow/logging/config" + "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) + } + 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 *validatorIndexOpt >= len(conf.GenesisDoc.Validators) { + 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 + printf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress) + } + + if *validatorPassphraseOpt != "" { + conf.ValidatorPassphrase = validatorPassphraseOpt + } + + if *validatorMonikerOpt != "" { + conf.Tendermint.Moniker = *validatorMonikerOpt + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + kern, err := conf.Kernel(ctx) + if err != nil { + fatalf("could not create Burrow kernel: %v", err) + } + + err = kern.Boot() + if err != nil { + fatalf("could not boot Burrow kernel: %v", err) + } + kern.WaitForShutdown() + } +} diff --git a/cmd/burrow/main.go b/cmd/burrow/main.go index ef1956c6b5acfa63bac393e2006cb32f02db005b..6c57758674250090e19c3d0f386248942bf10cb4 100644 --- a/cmd/burrow/main.go +++ b/cmd/burrow/main.go @@ -1,23 +1,10 @@ package main import ( - "context" "fmt" - "io/ioutil" "os" - "strings" - acm "github.com/hyperledger/burrow/account" - "github.com/hyperledger/burrow/config" - "github.com/hyperledger/burrow/config/source" - "github.com/hyperledger/burrow/execution" - "github.com/hyperledger/burrow/genesis" - "github.com/hyperledger/burrow/genesis/spec" - "github.com/hyperledger/burrow/keys" - "github.com/hyperledger/burrow/keys/mock" - "github.com/hyperledger/burrow/logging" - logging_config "github.com/hyperledger/burrow/logging/config" - "github.com/hyperledger/burrow/logging/config/presets" + "github.com/hyperledger/burrow/cmd/burrow/commands" "github.com/hyperledger/burrow/project" "github.com/jawher/mow.cli" ) @@ -39,373 +26,15 @@ func burrow() *cli.Cli { } } - app.Command("serve", "", - 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") + app.Command("start", "Start a Burrow node", + commands.Start) - 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) - } - 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 *validatorIndexOpt >= len(conf.GenesisDoc.Validators) { - 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 - printf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress) - } - - if *validatorPassphraseOpt != "" { - conf.ValidatorPassphrase = validatorPassphraseOpt - } - - if *validatorMonikerOpt != "" { - conf.Tendermint.Moniker = *validatorMonikerOpt - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - kern, err := conf.Kernel(ctx) - if err != nil { - fatalf("could not create Burrow kernel: %v", err) - } - - err = kern.Boot() - if err != nil { - fatalf("could not boot Burrow kernel: %v", err) - } - kern.WaitForShutdown() - } - }) - - app.Command("spec", - "Build a GenesisSpec that acts as a template for a GenesisDoc and the configure command", - func(cmd *cli.Cmd) { - tomlOpt := cmd.BoolOpt("t toml", false, "Emit GenesisSpec as TOML rather than the "+ - "default JSON") - - baseOpt := cmd.StringsOpt("b 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") - - fullOpt := cmd.IntOpt("f full-accounts", 1, "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", 1, "Number of preset Participant type accounts") - chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name") - - cmd.Spec = "[--base][--full-accounts] [--validator-accounts] [--root-accounts] [--developer-accounts] " + - "[--participant-accounts] [--chain-name] [--toml]" - - cmd.Action = func() { - specs := make([]spec.GenesisSpec, 0, *participantsOpt+*fullOpt) - for _, baseSpec := range *baseOpt { - genesisSpec := new(spec.GenesisSpec) - err := source.FromFile(baseSpec, genesisSpec) - if err != nil { - fatalf("could not read GenesisSpec: %v", err) - } - specs = append(specs, *genesisSpec) - } - for i := 0; i < *fullOpt; i++ { - specs = append(specs, spec.FullAccount(i)) - } - for i := 0; i < *validatorOpt; i++ { - specs = append(specs, spec.ValidatorAccount(i)) - } - for i := 0; i < *rootOpt; i++ { - specs = append(specs, spec.RootAccount(i)) - } - for i := 0; i < *developerOpt; i++ { - specs = append(specs, spec.DeveloperAccount(i)) - } - for i := 0; i < *participantsOpt; i++ { - specs = append(specs, spec.ParticipantAccount(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)) - } - } - }) + app.Command("spec", "Build a GenesisSpec that acts as a template for a GenesisDoc and the configure command", + commands.Spec) app.Command("configure", "Create Burrow configuration by consuming a GenesisDoc or GenesisSpec, creating keys, and emitting the config", - 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-out", false, "Emit config in JSON rather than TOML "+ - "suitable for further processing or forming a separate genesis.json GenesisDoc") - - 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") - - 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") - - keysTemplateOpt := cmd.StringOpt("z keys-template", mock.DefaultDumpKeysFormat, - fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+ - "file specified with --generate-keys. Default:\n%s", mock.LeftTemplateDelim, mock.RightTemplateDelim, - mock.DefaultDumpKeysFormat)) - - 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.") - - 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.") - - 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-out] " + - "[--logging=<logging program>] [--describe-logging] [--debug]" - - 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 *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()) - } - 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 *generateKeysOpt != "" { - keyClient := mock.NewMockKeyClient() - conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient) - if err != nil { - fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err) - } - - secretKeysString, err := keyClient.DumpKeys(*keysTemplateOpt) - if err != nil { - fatalf("Could not dump keys: %v", err) - } - err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700) - if err != nil { - fatalf("Could not write secret keys: %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) - } - } 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, - } - } - - 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.") - } - 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) - } - err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700) - if err != nil { - fatalf("Could not write GenesisDoc JSON: %v", err) - } - conf.GenesisDoc = nil - } - if *jsonOutOpt { - os.Stdout.WriteString(conf.JSONString()) - } else { - os.Stdout.WriteString(conf.TOMLString()) - } - } - }) - - app.Command("help", - "Get more detailed or exhaustive options of selected commands or flags.", - func(cmd *cli.Cmd) { - - cmd.Spec = "[--participant-accounts] [--full-accounts] [--toml]" - - cmd.Action = func() { - } - }) + commands.Configure) return app } - -// 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) -} - -func burrowConfigProvider(configFile string) source.ConfigProvider { - return source.FirstOf( - // Will fail if file doesn't exist, but still skipped it configFile == "" - source.File(configFile, false), - source.Environment(config.DefaultBurrowConfigJSONEnvironmentVariable), - // Try working directory - source.File(config.DefaultBurrowConfigTOMLFileName, true), - source.Default(config.DefaultBurrowConfig())) -} - -func genesisDocProvider(genesisFile string, skipNonExistent bool) source.ConfigProvider { - return source.NewConfigProvider(fmt.Sprintf("genesis file at %s", genesisFile), - source.ShouldSkipFile(genesisFile, skipNonExistent), - func(baseConfig interface{}) error { - conf, ok := baseConfig.(*config.BurrowConfig) - if !ok { - return fmt.Errorf("config passed was not BurrowConfig") - } - if conf.GenesisDoc != nil { - return fmt.Errorf("sourcing GenesisDoc from file %v, but GenesisDoc was defined in earlier "+ - "config source, only specify GenesisDoc in one place", genesisFile) - } - genesisDoc := new(genesis.GenesisDoc) - err := source.FromFile(genesisFile, genesisDoc) - if err != nil { - return err - } - conf.GenesisDoc = genesisDoc - return nil - }) -} diff --git a/genesis/spec/genesis_spec.go b/genesis/spec/genesis_spec.go index c037c5c4f7e6eab92241caed7c41eb1b7d68d1fb..11549a0171f645cf1f2f4a194500170422cdc3c2 100644 --- a/genesis/spec/genesis_spec.go +++ b/genesis/spec/genesis_spec.go @@ -24,23 +24,24 @@ const DefaultAmountBonded uint64 = 10000 // by interacting with the KeysClient it is passed and other information not known at // specification time type GenesisSpec struct { - GenesisTime *time.Time `json:",omitempty"` - ChainName string `json:",omitempty"` - Salt []byte `json:",omitempty"` - GlobalPermissions []string `json:",omitempty"` - Accounts []TemplateAccount `json:",omitempty"` + GenesisTime *time.Time `json:",omitempty" toml:",omitempty"` + ChainName string `json:",omitempty" toml:",omitempty"` + Salt []byte `json:",omitempty" toml:",omitempty"` + GlobalPermissions []string `json:",omitempty" toml:",omitempty"` + Accounts []TemplateAccount `json:",omitempty" toml:",omitempty"` } type TemplateAccount struct { + // Template accounts sharing a name will be merged when merging genesis specs + Name string `json:",omitempty" toml:",omitempty"` // Address is convenient to have in file for reference, but otherwise ignored since derived from PublicKey - Address *acm.Address `json:",omitempty"` - PublicKey *acm.PublicKey `json:",omitempty"` - Amount *uint64 `json:",omitempty"` + Address *acm.Address `json:",omitempty" toml:",omitempty"` + PublicKey *acm.PublicKey `json:",omitempty" toml:",omitempty"` + Amount *uint64 `json:",omitempty" toml:",omitempty"` // If any bonded amount then this account is also a Validator - AmountBonded *uint64 `json:",omitempty"` - Name string `json:",omitempty"` - Permissions []string `json:",omitempty"` - Roles []string `json:",omitempty"` + AmountBonded *uint64 `json:",omitempty" toml:",omitempty"` + Permissions []string `json:",omitempty" toml:",omitempty"` + Roles []string `json:",omitempty" toml:",omitempty"` } func (ta TemplateAccount) Validator(keyClient keys.KeyClient, index int) (*genesis.Validator, error) { diff --git a/genesis/spec/presets.go b/genesis/spec/presets.go index 0298a44760bf7f04ad58886555de083bcd94b23d..312ec7a79a78888f687210a56e218c32ad16a4e4 100644 --- a/genesis/spec/presets.go +++ b/genesis/spec/presets.go @@ -1,8 +1,6 @@ package spec import ( - "fmt" - "sort" "github.com/hyperledger/burrow/permission" @@ -11,13 +9,13 @@ import ( // Files here can be used as starting points for building various 'chain types' but are otherwise // a fairly unprincipled collection of GenesisSpecs that we find useful in testing and development -func FullAccount(index int) GenesisSpec { +func FullAccount(name string) GenesisSpec { // Inheriting from the arbitrary figures used by monax tool for now amount := uint64(99999999999999) amountBonded := uint64(9999999999) return GenesisSpec{ Accounts: []TemplateAccount{{ - Name: fmt.Sprintf("Full_%v", index), + Name: name, Amount: &amount, AmountBonded: &amountBonded, Permissions: []string{permission.AllString}, @@ -26,12 +24,12 @@ func FullAccount(index int) GenesisSpec { } } -func RootAccount(index int) GenesisSpec { +func RootAccount(name string) GenesisSpec { // Inheriting from the arbitrary figures used by monax tool for now amount := uint64(99999999999999) return GenesisSpec{ Accounts: []TemplateAccount{{ - Name: fmt.Sprintf("Root_%v", index), + Name: name, Amount: &amount, Permissions: []string{permission.AllString}, }, @@ -39,12 +37,12 @@ func RootAccount(index int) GenesisSpec { } } -func ParticipantAccount(index int) GenesisSpec { +func ParticipantAccount(name string) GenesisSpec { // Inheriting from the arbitrary figures used by monax tool for now amount := uint64(9999999999) return GenesisSpec{ Accounts: []TemplateAccount{{ - Name: fmt.Sprintf("Participant_%v", index), + Name: name, Amount: &amount, Permissions: []string{permission.SendString, permission.CallString, permission.NameString, permission.HasRoleString}, @@ -52,12 +50,12 @@ func ParticipantAccount(index int) GenesisSpec { } } -func DeveloperAccount(index int) GenesisSpec { +func DeveloperAccount(name string) GenesisSpec { // Inheriting from the arbitrary figures used by monax tool for now amount := uint64(9999999999) return GenesisSpec{ Accounts: []TemplateAccount{{ - Name: fmt.Sprintf("Developer_%v", index), + Name: name, Amount: &amount, Permissions: []string{permission.SendString, permission.CallString, permission.CreateContractString, permission.CreateAccountString, permission.NameString, permission.HasRoleString, @@ -66,13 +64,13 @@ func DeveloperAccount(index int) GenesisSpec { } } -func ValidatorAccount(index int) GenesisSpec { +func ValidatorAccount(name string) GenesisSpec { // Inheriting from the arbitrary figures used by monax tool for now amount := uint64(9999999999) amountBonded := amount - 1 return GenesisSpec{ Accounts: []TemplateAccount{{ - Name: fmt.Sprintf("Validator_%v", index), + Name: name, Amount: &amount, AmountBonded: &amountBonded, Permissions: []string{permission.BondString}, @@ -83,7 +81,7 @@ func ValidatorAccount(index int) GenesisSpec { func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec { mergedGenesisSpec := GenesisSpec{} // We will deduplicate and merge global permissions flags - permSet := make(map[string]bool) + permSet := make(map[string]struct{}) for _, genesisSpec := range genesisSpecs { // We'll overwrite chain name for later specs @@ -97,11 +95,11 @@ func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec { } for _, permString := range genesisSpec.GlobalPermissions { - permSet[permString] = true + permSet[permString] = struct{}{} } mergedGenesisSpec.Salt = append(mergedGenesisSpec.Salt, genesisSpec.Salt...) - mergedGenesisSpec.Accounts = append(mergedGenesisSpec.Accounts, genesisSpec.Accounts...) + mergedGenesisSpec.Accounts = mergeAccounts(mergedGenesisSpec.Accounts, genesisSpec.Accounts) } mergedGenesisSpec.GlobalPermissions = make([]string, 0, len(permSet)) @@ -115,3 +113,73 @@ func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec { return mergedGenesisSpec } + +// Merge accounts by adding to base list or updating previously named account +func mergeAccounts(bases, overrides []TemplateAccount) []TemplateAccount { + indexOfBase := make(map[string]int, len(bases)) + for i, ta := range bases { + if ta.Name != "" { + indexOfBase[ta.Name] = i + } + } + + for _, override := range overrides { + if override.Name != "" { + if i, ok := indexOfBase[override.Name]; ok { + bases[i] = mergeAccount(bases[i], override) + continue + } + } + bases = append(bases, override) + } + return bases +} + +func mergeAccount(base, override TemplateAccount) TemplateAccount { + if override.Address != nil { + base.Address = override.Address + } + if override.PublicKey != nil { + base.PublicKey = override.PublicKey + } + if override.Name != "" { + base.Name = override.Name + } + + base.Amount = addUint64Pointers(base.Amount, override.Amount) + base.AmountBonded = addUint64Pointers(base.AmountBonded, override.AmountBonded) + + base.Permissions = mergeStrings(base.Permissions, override.Permissions) + base.Roles = mergeStrings(base.Roles, override.Roles) + return base +} + +func mergeStrings(as, bs []string) []string { + var strs []string + strSet := make(map[string]struct{}) + for _, a := range as { + strSet[a] = struct{}{} + } + for _, b := range bs { + strSet[b] = struct{}{} + } + for str := range strSet { + strs = append(strs, str) + } + sort.Strings(strs) + return strs +} + +func addUint64Pointers(a, b *uint64) *uint64 { + if a == nil && b == nil { + return nil + } + amt := uint64(0) + if a != nil { + amt += *a + } + if b != nil { + amt += *b + } + return &amt +} diff --git a/genesis/spec/presets_test.go b/genesis/spec/presets_test.go index dc65fd9e92c34671439f0d5e21744ee2b83db7dc..b8a40cd00be4c59e487302f48daf1db3053fc674 100644 --- a/genesis/spec/presets_test.go +++ b/genesis/spec/presets_test.go @@ -11,14 +11,11 @@ import ( func TestMergeGenesisSpecAccounts(t *testing.T) { keyClient := mock.NewMockKeyClient() - gs := MergeGenesisSpecs(FullAccount(0), ParticipantAccount(1), ParticipantAccount(2)) + gs := MergeGenesisSpecs(FullAccount("0"), ParticipantAccount("1"), ParticipantAccount("2")) gd, err := gs.GenesisDoc(keyClient) require.NoError(t, err) assert.Len(t, gd.Validators, 1) assert.Len(t, gd.Accounts, 3) - //bs, err := gd.JSONBytes() - //require.NoError(t, err) - //fmt.Println(string(bs)) } func TestMergeGenesisSpecGlobalPermissions(t *testing.T) { @@ -33,3 +30,63 @@ func TestMergeGenesisSpecGlobalPermissions(t *testing.T) { assert.Equal(t, []string{permission.CreateAccountString, permission.HasRoleString, permission.SendString}, gsMerged.GlobalPermissions) } + +func TestMergeGenesisSpecsRepeatedAccounts(t *testing.T) { + name1 := "Party!" + name3 := "Counter!" + + amt1 := uint64(5) + amt2 := uint64(2) + amt3 := uint64(9) + + gs1 := GenesisSpec{ + Accounts: []TemplateAccount{ + { + Name: name1, + Amount: &amt1, + Permissions: []string{permission.SendString, permission.CreateAccountString, permission.HasRoleString}, + Roles: []string{"fooer"}, + }, + }, + } + gs2 := GenesisSpec{ + Accounts: []TemplateAccount{ + { + Name: name1, + Amount: &amt2, + Permissions: []string{permission.SendString, permission.CreateAccountString}, + Roles: []string{"barer"}, + }, + }, + } + gs3 := GenesisSpec{ + Accounts: []TemplateAccount{ + { + Name: name3, + Amount: &amt3, + }, + }, + } + + gsMerged := MergeGenesisSpecs(gs1, gs2, gs3) + bsMerged, err := gsMerged.JSONBytes() + require.NoError(t, err) + + amtExpected := amt1 + amt2 + gsExpected := GenesisSpec{ + Accounts: []TemplateAccount{ + { + Name: name1, + Amount: &amtExpected, + Permissions: []string{permission.CreateAccountString, permission.HasRoleString, permission.SendString}, + Roles: []string{"barer", "fooer"}, + }, + gs3.Accounts[0], + }, + } + bsExpected, err := gsExpected.JSONBytes() + require.NoError(t, err) + if !assert.Equal(t, string(bsExpected), string(bsMerged)) { + t.Logf("Expected:\n%s\n\nActual:\n%s", string(bsExpected), string(bsMerged)) + } +} diff --git a/keys/mock/key_client_mock.go b/keys/mock/key_client_mock.go index d2c78231f945cdf3d7db952dc21933f016492298..47bdca9aeb9dba5840d2da98ba6c692db89c9005 100644 --- a/keys/mock/key_client_mock.go +++ b/keys/mock/key_client_mock.go @@ -15,20 +15,23 @@ package mock import ( + "bytes" "crypto/rand" + "encoding/base64" "fmt" - - "bytes" "text/template" - "encoding/base64" + "encoding/json" + + "reflect" acm "github.com/hyperledger/burrow/account" . "github.com/hyperledger/burrow/keys" "github.com/pkg/errors" "github.com/tendermint/ed25519" - crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto" "github.com/tmthrgd/go-hex" + "github.com/wayn3h0/go-uuid" "golang.org/x/crypto/ripemd160" ) @@ -48,16 +51,33 @@ const DefaultDumpKeysFormat = `{ { "Name": "<< $key.Name >>", "Address": "<< $key.Address >>", - "PublicKey": "<< $key.PublicKeyBase64 >>", - "PrivateKey": "<< $key.PrivateKeyBase64 >>" + "PublicKey": "<< base64 $key.PublicKey >>", + "PrivateKey": "<< base64 $key.PrivateKey >>" }<< end >> ] }` +const KubernetesKeyDumpFormat = `keysFiles:<< range $index, $key := . >> + key-<< printf "%03d" $index >>: << base64 $key.MonaxKeyJSON >><< end >> +keysAddresses:<< range $index, $key := . >> + key-<< printf "%03d" $index >>: << $key.Address >><< end >> +validatorAddresses:<< range $index, $key := . >> + - << $key.Address >><< end >> +` + const LeftTemplateDelim = "<<" const RightTemplateDelim = ">>" -var DefaultDumpKeysTemplate = template.Must(template.New("MockKeyClient_DumpKeys"). +var templateFuncs template.FuncMap = map[string]interface{}{ + "base64": func(rv reflect.Value) string { + return encode(rv, base64.StdEncoding.EncodeToString) + }, + "hex": func(rv reflect.Value) string { + return encode(rv, hex.EncodeUpperToString) + }, +} + +var DefaultDumpKeysTemplate = template.Must(template.New("MockKeyClient_DumpKeys").Funcs(templateFuncs). Delims(LeftTemplateDelim, RightTemplateDelim). Parse(DefaultDumpKeysFormat)) @@ -110,20 +130,31 @@ func (mockKey *MockKey) Sign(message []byte) (acm.Signature, error) { return acm.SignatureFromBytes(ed25519.Sign(&privateKey, message)[:]) } -func (mockKey *MockKey) PrivateKeyBase64() string { - return base64.StdEncoding.EncodeToString(mockKey.PrivateKey[:]) -} - -func (mockKey *MockKey) PrivateKeyHex() string { - return hex.EncodeUpperToString(mockKey.PrivateKey[:]) -} - -func (mockKey *MockKey) PublicKeyBase64() string { - return base64.StdEncoding.EncodeToString(mockKey.PublicKey) +// TODO: remove after merging keys taken from there to match serialisation +type plainKeyJSON struct { + Id []byte + Type string + Address string + PrivateKey []byte } -func (mockKey *MockKey) PublicKeyHex() string { - return hex.EncodeUpperToString(mockKey.PublicKey) +// Returns JSON string compatible with that stored by monax-keys +func (mockKey *MockKey) MonaxKeyJSON() string { + id, err := uuid.NewRandom() + if err != nil { + return errors.Wrap(err, "could not create monax key json").Error() + } + jsonKey := plainKeyJSON{ + Id: []byte(id.String()), + Address: mockKey.Address.String(), + Type: string(KeyTypeEd25519Ripemd160), + PrivateKey: mockKey.PrivateKey, + } + bs, err := json.Marshal(jsonKey) + if err != nil { + return errors.Wrap(err, "could not create monax key json").Error() + } + return string(bs) } //--------------------------------------------------------------------- @@ -183,9 +214,10 @@ func (mkc *MockKeyClient) HealthCheck() error { } func (mkc *MockKeyClient) DumpKeys(templateString string) (string, error) { - tmpl, err := template.New("DumpKeys").Delims(LeftTemplateDelim, RightTemplateDelim).Parse(templateString) + tmpl, err := template.New("DumpKeys").Delims(LeftTemplateDelim, RightTemplateDelim).Funcs(templateFuncs). + Parse(templateString) if err != nil { - errors.Wrap(err, "could not dump keys to template") + return "", errors.Wrap(err, "could not dump keys to template") } buf := new(bytes.Buffer) keys := make([]*MockKey, 0, len(mkc.knownKeys)) @@ -198,3 +230,14 @@ func (mkc *MockKeyClient) DumpKeys(templateString string) (string, error) { } return buf.String(), nil } + +func encode(rv reflect.Value, encoder func([]byte) string) string { + switch rv.Kind() { + case reflect.Slice: + return encoder(rv.Bytes()) + case reflect.String: + return encoder([]byte(rv.String())) + default: + panic(fmt.Errorf("could not convert %#v to bytes to encode", rv)) + } +} diff --git a/keys/mock/key_client_mock_test.go b/keys/mock/key_client_mock_test.go index ffcec0019c000eecfa116ec1be3e2b275cb6c992..9a5cbef3f520c7c17d16592f51dfb7aae12f7227 100644 --- a/keys/mock/key_client_mock_test.go +++ b/keys/mock/key_client_mock_test.go @@ -5,6 +5,8 @@ import ( "encoding/json" + "fmt" + "github.com/hyperledger/burrow/keys" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,3 +29,29 @@ func TestMockKeyClient_DumpKeys(t *testing.T) { require.NoError(t, err) assert.Equal(t, string(bs), dump) } + +func TestMockKeyClient_DumpKeysKubernetes(t *testing.T) { + keyClient := NewMockKeyClient() + _, err := keyClient.Generate("foo", keys.KeyTypeEd25519Ripemd160) + require.NoError(t, err) + _, err = keyClient.Generate("foobar", keys.KeyTypeEd25519Ripemd160) + require.NoError(t, err) + dump, err := keyClient.DumpKeys(KubernetesKeyDumpFormat) + require.NoError(t, err) + fmt.Println(dump) +} + +func TestMockKey_MonaxKeyJSON(t *testing.T) { + key, err := newMockKey("monax-key-test") + require.NoError(t, err) + monaxKey := key.MonaxKeyJSON() + t.Logf("key is: %v", monaxKey) + keyJSON := &plainKeyJSON{} + err = json.Unmarshal([]byte(monaxKey), keyJSON) + require.NoError(t, err) + // byte length of UUID string = 16 * 2 + 4 = 36 + assert.Len(t, keyJSON.Id, 36) + assert.Equal(t, key.Address.String(), keyJSON.Address) + assert.Equal(t, key.PrivateKey, keyJSON.PrivateKey) + assert.Equal(t, string(keys.KeyTypeEd25519Ripemd160), keyJSON.Type) +} diff --git a/vendor/github.com/wayn3h0/go-uuid/LICENSE b/vendor/github.com/wayn3h0/go-uuid/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..fad0aa6350da088c679b81e73dee76ef873491da --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/LICENSE @@ -0,0 +1,13 @@ +Copyright 2015 Wayne Ho. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/wayn3h0/go-uuid/domain.go b/vendor/github.com/wayn3h0/go-uuid/domain.go new file mode 100644 index 0000000000000000000000000000000000000000..ce09399bcae3b33d13fd12648d20594b9952530a --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/domain.go @@ -0,0 +1,14 @@ +package uuid + +import ( + "github.com/wayn3h0/go-uuid/internal/dcesecurity" +) + +// Domain represents the identifier for a local domain +type Domain byte + +// Domains. +const ( + DomainUser = Domain(dcesecurity.User) // POSIX UID domain + DomainGroup = Domain(dcesecurity.Group) // POSIX GID domain +) diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go new file mode 100644 index 0000000000000000000000000000000000000000..9de07243d9483e479daa7cf6be52b36496d90cf7 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go @@ -0,0 +1,37 @@ +package dcesecurity + +import ( + "encoding/binary" + "errors" + "os" + + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/timebased" + "github.com/wayn3h0/go-uuid/internal/version" +) + +// Generate returns a new DCE security uuid. +func New(domain Domain) ([]byte, error) { + uuid, err := timebased.New() + if err != nil { + return nil, err + } + + switch domain { + case User: + uid := os.Getuid() + binary.BigEndian.PutUint32(uuid[0:], uint32(uid)) // network byte order + case Group: + gid := os.Getgid() + binary.BigEndian.PutUint32(uuid[0:], uint32(gid)) // network byte order + default: + return nil, errors.New("uuid: domain is invalid") + } + + // set version(v2) + version.Set(uuid, version.DCESecurity) + // set layout(RFC4122) + layout.Set(uuid, layout.RFC4122) + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go new file mode 100644 index 0000000000000000000000000000000000000000..38eae8301fdb4a49fe10e50b74ef64a666919a51 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go @@ -0,0 +1,9 @@ +package dcesecurity + +// Domain represents the identifier for a local domain +type Domain byte + +const ( + User Domain = iota + 1 // POSIX UID domain + Group // POSIX GID domain +) diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go b/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go new file mode 100644 index 0000000000000000000000000000000000000000..65a84cc463e4e429bbcea37024c1cffc56859daa --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go @@ -0,0 +1,12 @@ +package layout + +// Layout represents the layout of UUID. See page 5 in RFC 4122. +type Layout byte + +const ( + Invalid Layout = iota // Invalid + NCS // Reserved, NCS backward compatibility. (Values: 0x00-0x07) + RFC4122 // The variant specified in RFC 4122. (Values: 0x08-0x0b) + Microsoft // Reserved, Microsoft Corporation backward compatibility. (Values: 0x0c-0x0d) + Future // Reserved for future definition. (Values: 0x0e-0x0f) +) diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go b/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go new file mode 100644 index 0000000000000000000000000000000000000000..7c5fde7beb4acd4386126cbbe93e5cf8040e8b67 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go @@ -0,0 +1,33 @@ +package layout + +// Set sets the layout for uuid. +func Set(uuid []byte, layout Layout) { + switch layout { + case NCS: + uuid[8] = (uuid[8] | 0x00) & 0x0f // Msb0=0 + case RFC4122: + uuid[8] = (uuid[8] | 0x80) & 0x8f // Msb0=1, Msb1=0 + case Microsoft: + uuid[8] = (uuid[8] | 0xc0) & 0xcf // Msb0=1, Msb1=1, Msb2=0 + case Future: + uuid[8] = (uuid[8] | 0xe0) & 0xef // Msb0=1, Msb1=1, Msb2=1 + default: + panic("uuid: layout is invalid") + } +} + +// Get returns layout of uuid. +func Get(uuid []byte) Layout { + switch { + case (uuid[8] & 0x80) == 0x00: + return NCS + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + } + + return Invalid +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go new file mode 100644 index 0000000000000000000000000000000000000000..a03e6c71d68cd9f10cbe6591af938122ed803e71 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go @@ -0,0 +1,33 @@ +package md5 + +import ( + "crypto/md5" + + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/version" +) + +// New returns a new name-based uses SHA-1 hashing uuid. +func New(namespace, name string) ([]byte, error) { + hash := md5.New() + _, err := hash.Write([]byte(namespace)) + if err != nil { + return nil, err + } + _, err = hash.Write([]byte(name)) + if err != nil { + return nil, err + } + + sum := hash.Sum(nil) + + uuid := make([]byte, 16) + copy(uuid, sum) + + // set version(v3) + version.Set(uuid, version.NameBasedMD5) + // set layout(RFC4122) + layout.Set(uuid, layout.RFC4122) + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go new file mode 100644 index 0000000000000000000000000000000000000000..f4202676ad56349cf0dee76ef3adae53805f2b66 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go @@ -0,0 +1,9 @@ +package namebased + +// Standard Namespaces +const ( + NamespaceDNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + NamespaceURL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8" + NamespaceOID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8" + NamespaceX500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8" +) diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go new file mode 100644 index 0000000000000000000000000000000000000000..14a8eeb01c607720d0b2acfb230f3dc431e2d629 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go @@ -0,0 +1,33 @@ +package sha1 + +import ( + "crypto/sha1" + + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/version" +) + +// New returns a new name-based uses SHA-1 hashing uuid. +func New(namespace, name string) ([]byte, error) { + hash := sha1.New() + _, err := hash.Write([]byte(namespace)) + if err != nil { + return nil, err + } + _, err = hash.Write([]byte(name)) + if err != nil { + return nil, err + } + + sum := hash.Sum(nil) + + uuid := make([]byte, 16) + copy(uuid, sum) + + // set version(v5) + version.Set(uuid, version.NameBasedSHA1) + // set layout(RFC4122) + layout.Set(uuid, layout.RFC4122) + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go b/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go new file mode 100644 index 0000000000000000000000000000000000000000..59354280800f18125241c15b9e941198ff2cc0e8 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go @@ -0,0 +1,27 @@ +package random + +import ( + "crypto/rand" + + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/version" +) + +// New returns a new randomly uuid. +func New() ([]byte, error) { + uuid := make([]byte, 16) + n, err := rand.Read(uuid[:]) + if err != nil { + return nil, err + } + if n != len(uuid) { + return nil, err + } + + // set version(v4) + version.Set(uuid, version.Random) + // set layout(RFC4122) + layout.Set(uuid, layout.RFC4122) + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go b/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go new file mode 100644 index 0000000000000000000000000000000000000000..43541567cf1eaf24fdc4df388608f316e51da635 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go @@ -0,0 +1,92 @@ +package timebased + +import ( + "crypto/rand" + "encoding/binary" + "net" + "sync" + "time" + + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/version" +) + +const ( + // Intervals bewteen 1/1/1970 and 15/10/1582 (Julain days of 1 Jan 1970 - Julain days of 15 Oct 1582) * 100-Nanoseconds Per Day + intervals = (2440587 - 2299160) * 86400 * 10000000 +) + +var ( + lastGenerated time.Time // last generated time + clockSequence uint16 // clock sequence for same tick + nodeID []byte // node id (MAC Address) + locker sync.Mutex // global lock +) + +// New returns a new time-based uuid. +func New() ([]byte, error) { + // Get and release a global lock + locker.Lock() + defer locker.Unlock() + + uuid := make([]byte, 16) + + // get timestamp + now := time.Now().UTC() + timestamp := uint64(now.UnixNano()/100) + intervals // get timestamp + if !now.After(lastGenerated) { + clockSequence++ // last generated time known, then just increment clock sequence + } else { + b := make([]byte, 2) + _, err := rand.Read(b) + if err != nil { + return nil, err + } + clockSequence = uint16(int(b[0])<<8 | int(b[1])) // set to a random value (network byte order) + } + + lastGenerated = now // remember the last generated time + + timeLow := uint32(timestamp & 0xffffffff) + timeMiddle := uint16((timestamp >> 32) & 0xffff) + timeHigh := uint16((timestamp >> 48) & 0xfff) + + // network byte order(BigEndian) + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMiddle) + binary.BigEndian.PutUint16(uuid[6:], timeHigh) + binary.BigEndian.PutUint16(uuid[8:], clockSequence) + + // get node id(mac address) + if nodeID == nil { + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + for _, i := range interfaces { + if len(i.HardwareAddr) >= 6 { + nodeID = make([]byte, 6) + copy(nodeID, i.HardwareAddr) + break + } + } + + if nodeID == nil { + nodeID = make([]byte, 6) + _, err := rand.Read(nodeID) + if err != nil { + return nil, err + } + } + } + + copy(uuid[10:], nodeID) + + // set version(v1) + version.Set(uuid, version.TimeBased) + // set layout(RFC4122) + layout.Set(uuid, layout.RFC4122) + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go b/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go new file mode 100644 index 0000000000000000000000000000000000000000..2862711a022e9f06fa763ea6546700e7765f2aed --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go @@ -0,0 +1,29 @@ +package version + +// Set sets the version for uuid. +func Set(uuid []byte, version Version) { + switch version { + case TimeBased: + uuid[6] = (uuid[6] | 0x10) & 0x1f + case DCESecurity: + uuid[6] = (uuid[6] | 0x20) & 0x2f + case NameBasedMD5: + uuid[6] = (uuid[6] | 0x30) & 0x3f + case Random: + uuid[6] = (uuid[6] | 0x40) & 0x4f + case NameBasedSHA1: + uuid[6] = (uuid[6] | 0x50) & 0x5f + default: + panic("uuid: version is unknown") + } +} + +// Get gets the version of uuid. +func Get(uuid []byte) Version { + version := uuid[6] >> 4 + if version > 0 && version < 6 { + return Version(version) + } + + return Unknown +} diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go b/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go new file mode 100644 index 0000000000000000000000000000000000000000..10ab52cfa15cde59be546f2c21dc49e24b79103c --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go @@ -0,0 +1,14 @@ +package version + +// Version represents the version of UUID. See page 7 in RFC 4122. +type Version byte + +// Version List +const ( + Unknown Version = iota // Unknwon + TimeBased // V1: The time-based version + DCESecurity // V2: The DCE security version, with embedded POSIX UIDs + NameBasedMD5 // V3: The name-based version that uses MD5 hashing + Random // V4: The randomly or pseudo-randomly generated version + NameBasedSHA1 // V5: The name-based version that uses SHA-1 hashing +) diff --git a/vendor/github.com/wayn3h0/go-uuid/layout.go b/vendor/github.com/wayn3h0/go-uuid/layout.go new file mode 100644 index 0000000000000000000000000000000000000000..7656df5d163e6e15a85aeaef1d7ec9605d3885f0 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/layout.go @@ -0,0 +1,33 @@ +package uuid + +import ( + "github.com/wayn3h0/go-uuid/internal/layout" +) + +// Layout represents the layout of UUID. See page 5 in RFC 4122. +type Layout byte + +// Layouts. +const ( + LayoutInvalid = Layout(layout.Invalid) // Invalid + LayoutNCS = Layout(layout.NCS) // Reserved, NCS backward compatibility. (Values: 0x00-0x07) + LayoutRFC4122 = Layout(layout.RFC4122) // The variant specified in RFC 4122. (Values: 0x08-0x0b) + LayoutMicrosoft = Layout(layout.Microsoft) // Reserved, Microsoft Corporation backward compatibility. (Values: 0x0c-0x0d) + LayoutFuture = Layout(layout.Future) // Reserved for future definition. (Values: 0x0e-0x0f) +) + +// String returns English description of layout. +func (this Layout) String() string { + switch this { + case LayoutNCS: + return "Layout: Reserved For NCS" + case LayoutRFC4122: + return "Layout: RFC 4122" + case LayoutMicrosoft: + return "Layout: Reserved For Microsoft" + case LayoutFuture: + return "Layout: Reserved For Future" + default: + return "Layout: Invalid" + } +} diff --git a/vendor/github.com/wayn3h0/go-uuid/namespace.go b/vendor/github.com/wayn3h0/go-uuid/namespace.go new file mode 100644 index 0000000000000000000000000000000000000000..23b314a56f84c63c6c9788a226d261d5b5545316 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/namespace.go @@ -0,0 +1,13 @@ +package uuid + +import ( + "github.com/wayn3h0/go-uuid/internal/namebased" +) + +// Imports namespaces. +const ( + NamespaceDNS = namebased.NamespaceDNS // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + NamespaceURL = namebased.NamespaceURL // "6ba7b811-9dad-11d1-80b4-00c04fd430c8" + NamespaceOID = namebased.NamespaceOID // "6ba7b812-9dad-11d1-80b4-00c04fd430c8" + NamespaceX500 = namebased.NamespaceX500 // "6ba7b814-9dad-11d1-80b4-00c04fd430c8" +) diff --git a/vendor/github.com/wayn3h0/go-uuid/parse.go b/vendor/github.com/wayn3h0/go-uuid/parse.go new file mode 100644 index 0000000000000000000000000000000000000000..0847299c3cede87a69dea74f44860003d34b2722 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/parse.go @@ -0,0 +1,47 @@ +package uuid + +import ( + "encoding/hex" + "errors" + "fmt" +) + +// Parse parses the UUID string. +func Parse(str string) (UUID, error) { + length := len(str) + buffer := make([]byte, 16) + charIndexes := []int{} + switch length { + case 36: + if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' { + return Nil, fmt.Errorf("uuid: format of UUID string \"%s\" is invalid, it should be xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)", str) + } + charIndexes = []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} + case 32: + charIndexes = []int{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30} + default: + return Nil, fmt.Errorf("uuid: length of UUID string \"%s\" is invalid, it should be 36 (standard) or 32 (without dash)", str) + } + for i, v := range charIndexes { + if c, e := hex.DecodeString(str[v : v+2]); e != nil { + return Nil, fmt.Errorf("uuid: UUID string \"%s\" is invalid: %s", str, e.Error()) + } else { + buffer[i] = c[0] + } + } + + uuid := UUID{} + copy(uuid[:], buffer) + + if !uuid.Equal(Nil) { + if uuid.Layout() == LayoutInvalid { + return Nil, errors.New("uuid: layout is invalid") + } + + if uuid.Version() == VersionUnknown { + return Nil, errors.New("uuid: version is unknown") + } + } + + return uuid, nil +} diff --git a/vendor/github.com/wayn3h0/go-uuid/style.go b/vendor/github.com/wayn3h0/go-uuid/style.go new file mode 100644 index 0000000000000000000000000000000000000000..17b816a75c1d1b9ce8c7142b15a7fbbc527d6597 --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/style.go @@ -0,0 +1,22 @@ +package uuid + +// Style represents the style of UUID string. +type Style byte + +// Styles. +const ( + StyleStandard Style = iota + 1 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12, length: 36) + StyleWithoutDash // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (length: 32) +) + +// String returns English description of style. +func (this Style) String() string { + switch this { + case StyleStandard: + return "Style: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)" + case StyleWithoutDash: + return "Style: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + default: + return "Style: Unknown" + } +} diff --git a/vendor/github.com/wayn3h0/go-uuid/uuid.go b/vendor/github.com/wayn3h0/go-uuid/uuid.go new file mode 100644 index 0000000000000000000000000000000000000000..ea17ec284621868df10e5b288a28ccb1b7a8476b --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/uuid.go @@ -0,0 +1,147 @@ +package uuid + +import ( + "bytes" + "fmt" + + "github.com/wayn3h0/go-uuid/internal/dcesecurity" + "github.com/wayn3h0/go-uuid/internal/layout" + "github.com/wayn3h0/go-uuid/internal/namebased/md5" + "github.com/wayn3h0/go-uuid/internal/namebased/sha1" + "github.com/wayn3h0/go-uuid/internal/random" + "github.com/wayn3h0/go-uuid/internal/timebased" + "github.com/wayn3h0/go-uuid/internal/version" +) + +var ( + Nil = UUID{} // Nil UUID +) + +// NewTimeBased returns a new time based UUID (version 1). +func NewTimeBased() (UUID, error) { + u, err := timebased.New() + if err != nil { + return Nil, err + } + + uuid := UUID{} + copy(uuid[:], u) + + return uuid, nil +} + +// NewV1 same as NewTimeBased. +func NewV1() (UUID, error) { + return NewTimeBased() +} + +// NewDCESecurity returns a new DCE security UUID (version 2). +func NewDCESecurity(domain Domain) (UUID, error) { + u, err := dcesecurity.New(dcesecurity.Domain(domain)) + if err != nil { + return Nil, err + } + + uuid := UUID{} + copy(uuid[:], u) + + return uuid, nil +} + +// NewV2 same as NewDCESecurity. +func NewV2(domain Domain) (UUID, error) { + return NewDCESecurity(domain) +} + +// NewNameBasedMD5 returns a new name based UUID with MD5 hash (version 3). +func NewNameBasedMD5(namespace, name string) (UUID, error) { + u, err := md5.New(namespace, name) + if err != nil { + return Nil, err + } + + uuid := UUID{} + copy(uuid[:], u) + + return uuid, nil +} + +// NewV3 same as NewNameBasedMD5. +func NewV3(namespace, name string) (UUID, error) { + return NewNameBasedMD5(namespace, name) +} + +// NewRandom returns a new random UUID (version 4). +func NewRandom() (UUID, error) { + u, err := random.New() + if err != nil { + return Nil, err + } + + uuid := UUID{} + copy(uuid[:], u) + + return uuid, nil +} + +// NewV4 same as NewRandom. +func NewV4() (UUID, error) { + return NewRandom() +} + +// New same as NewRandom. +func New() (UUID, error) { + return NewRandom() +} + +// NewNameBasedSHA1 returns a new name based UUID with SHA1 hash (version 5). +func NewNameBasedSHA1(namespace, name string) (UUID, error) { + u, err := sha1.New(namespace, name) + if err != nil { + return Nil, err + } + + uuid := UUID{} + copy(uuid[:], u) + + return uuid, nil +} + +// NewV5 same as NewNameBasedSHA1. +func NewV5(namespace, name string) (UUID, error) { + return NewNameBasedSHA1(namespace, name) +} + +// UUID respresents an UUID type compliant with specification in RFC 4122. +type UUID [16]byte + +// Layout returns layout of UUID. +func (this UUID) Layout() Layout { + return Layout(layout.Get(this[:])) +} + +// Version returns version of UUID. +func (this UUID) Version() Version { + return Version(version.Get(this[:])) +} + +// Equal returns true if current uuid equal to passed uuid. +func (this UUID) Equal(another UUID) bool { + return bytes.EqualFold(this[:], another[:]) +} + +// Format returns the formatted string of UUID. +func (this UUID) Format(style Style) string { + switch style { + case StyleWithoutDash: + return fmt.Sprintf("%x", this[:]) + //case StyleStandard: + default: + return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", this[:4], this[4:6], this[6:8], this[8:10], this[10:]) + } +} + +// String returns the string of UUID with standard style(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 8-4-4-4-12). +func (this UUID) String() string { + return this.Format(StyleStandard) +} diff --git a/vendor/github.com/wayn3h0/go-uuid/version.go b/vendor/github.com/wayn3h0/go-uuid/version.go new file mode 100644 index 0000000000000000000000000000000000000000..af509e3a3bb170dc45576c415d38f3c6f0444ffe --- /dev/null +++ b/vendor/github.com/wayn3h0/go-uuid/version.go @@ -0,0 +1,45 @@ +package uuid + +import ( + "github.com/wayn3h0/go-uuid/internal/version" +) + +// Version represents the version of UUID. See page 7 in RFC 4122. +type Version byte + +// Versions. +const ( + VersionUnknown = Version(version.Unknown) // Unknown + VersionTimeBased = Version(version.TimeBased) // V1: The time-based version + VersionDCESecurity = Version(version.DCESecurity) // V2: The DCE security version, with embedded POSIX UIDs + VersionNameBasedMD5 = Version(version.NameBasedMD5) // V3: The name-based version that uses MD5 hashing + VersionRandom = Version(version.Random) // V4: The randomly or pseudo-randomly generated version + VersionNameBasedSHA1 = Version(version.NameBasedSHA1) // V5: The name-based version that uses SHA-1 hashing +) + +// Short names. +const ( + V1 = VersionTimeBased + V2 = VersionDCESecurity + V3 = VersionNameBasedMD5 + V4 = VersionRandom + V5 = VersionNameBasedSHA1 +) + +// String returns English description of version. +func (this Version) String() string { + switch this { + case VersionTimeBased: + return "Version 1: Time-Based" + case VersionDCESecurity: + return "Version 2: DCE Security With Embedded POSIX UIDs" + case VersionNameBasedMD5: + return "Version 3: Name-Based (MD5)" + case VersionRandom: + return "Version 4: Randomly OR Pseudo-Randomly Generated" + case VersionNameBasedSHA1: + return "Version 5: Name-Based (SHA-1)" + default: + return "Version: Unknown" + } +}