diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/sigsum-log-primary/README.md (renamed from cmd/sigsum_log_go/README.md) | 18 | ||||
-rw-r--r-- | cmd/sigsum-log-primary/main.go | 233 | ||||
-rw-r--r-- | cmd/sigsum-log-secondary/main.go | 175 | ||||
-rw-r--r-- | cmd/sigsum_log_go/.gitignore | 1 | ||||
-rw-r--r-- | cmd/sigsum_log_go/main.go | 223 |
5 files changed, 416 insertions, 234 deletions
diff --git a/cmd/sigsum_log_go/README.md b/cmd/sigsum-log-primary/README.md index 5c363f7..a824173 100644 --- a/cmd/sigsum_log_go/README.md +++ b/cmd/sigsum-log-primary/README.md @@ -1,11 +1,11 @@ -# Run Trillian + sigsum-log-go locally +# Run Trillian + sigsum-log-primary locally Trillian uses a database. So, we will need to set that up. It is documented [here](https://github.com/google/trillian#mysql-setup), and how to check that it is setup properly [here](https://github.com/google/certificate-transparency-go/blob/master/trillian/docs/ManualDeployment.md#data-storage). Other than the database we need Trillian log signer, Trillian log server, and -sigsum-log-go. sigsum-log-go has been tested with trillian v.1.3.13. +sigsum-log-primary. sigsum-log-primary has been tested with trillian v.1.3.13. ``` $ go install github.com/google/trillian/cmd/trillian_log_signer@v1.3.13 $ go install github.com/google/trillian/cmd/trillian_log_server@v1.3.13 @@ -30,25 +30,23 @@ $ createtree --admin_server localhost:6962 <tree id> ``` -Hang on to `<tree id>`. Our sigsum-log-go instance will use it when talking to +Hang on to `<tree id>`. Our sigsum-log-primary instance will use it when talking to the Trillian log server to specify which Merkle tree we are working against. (If you take a look in the `Trees` table you will see that the tree has been provisioned.) -We will also need a public key-pair for sigsum-log-go. +We will also need a public key-pair for sigsum-log-primary. ``` -$ go install git.sigsum.org/sigsum-log-go/cmd/tmp/keygen -$ ./keygen -sk: <sk> -vk: <vk> +$ go install git.sigsum.org/sigsum-go/cmd/sigsum-debug@latest +$ sigsum-debug key private | tee sk | sigsum-debug key public > vk ``` -Start sigsum-log-go: +Start sigsum-log-primary: ``` $ tree_id=<tree_id> $ sk=<sk> -$ sigsum_log_go --logtostderr -v 9 --http_endpoint localhost:6965 --log_rpc_server localhost:6962 --trillian_id $tree_id --key $sk +$ sigsum-log-primary --logtostderr -v 9 --http_endpoint localhost:6965 --log_rpc_server localhost:6962 --trillian_id $tree_id --key <(echo sk) ``` Quick test: diff --git a/cmd/sigsum-log-primary/main.go b/cmd/sigsum-log-primary/main.go new file mode 100644 index 0000000..f64643a --- /dev/null +++ b/cmd/sigsum-log-primary/main.go @@ -0,0 +1,233 @@ +// Package main provides a sigsum-log-primary binary +package main + +import ( + "context" + "encoding/hex" + "flag" + "fmt" + "net/http" + "os" + "os/signal" + "strings" + "sync" + "syscall" + "time" + + "github.com/google/trillian" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + + "git.sigsum.org/log-go/internal/db" + "git.sigsum.org/log-go/internal/node/primary" + "git.sigsum.org/log-go/internal/state" + "git.sigsum.org/log-go/internal/utils" + "git.sigsum.org/sigsum-go/pkg/client" + "git.sigsum.org/sigsum-go/pkg/dns" + "git.sigsum.org/sigsum-go/pkg/log" + "git.sigsum.org/sigsum-go/pkg/merkle" + "git.sigsum.org/sigsum-go/pkg/types" +) + +var ( + externalEndpoint = flag.String("external-endpoint", "localhost:6965", "host:port specification of where sigsum-log-primary serves clients") + internalEndpoint = flag.String("internal-endpoint", "localhost:6967", "host:port specification of where sigsum-log-primary serves other log nodes") + rpcBackend = flag.String("trillian-rpc-server", "localhost:6962", "host:port specification of where Trillian serves clients") + prefix = flag.String("url-prefix", "", "a prefix that precedes /sigsum/v0/<endpoint>") + trillianID = flag.Int64("tree-id", 0, "tree identifier in the Trillian database") + deadline = flag.Duration("deadline", time.Second*10, "deadline for backend requests") + key = flag.String("key", "", "path to file with hex-encoded Ed25519 private key") + witnesses = flag.String("witnesses", "", "comma-separated list of trusted witness public keys in hex") + maxRange = flag.Int64("max-range", 10, "maximum number of entries that can be retrived in a single request") + interval = flag.Duration("interval", time.Second*30, "interval used to rotate the log's cosigned STH") + shardStart = flag.Int64("shard-interval-start", 0, "start of shard interval since the UNIX epoch in seconds") + testMode = flag.Bool("test-mode", false, "run in test mode (Default: false)") + logFile = flag.String("log-file", "", "file to write logs to (Default: stderr)") + logLevel = flag.String("log-level", "info", "log level (Available options: debug, info, warning, error. Default: info)") + logColor = flag.Bool("log-color", false, "colored logging output (Default: false)") + secondaryURL = flag.String("secondary-url", "", "secondary node endpoint for fetching latest replicated tree head") + secondaryPubkey = flag.String("secondary-pubkey", "", "hex-encoded Ed25519 public key for secondary node") + + gitCommit = "unknown" +) + +func main() { + flag.Parse() + + if err := utils.SetupLogging(*logFile, *logLevel, *logColor); err != nil { + log.Fatal("setup logging: %v", err) + } + log.Info("log-go git-commit %s", gitCommit) + + // wait for clean-up before exit + var wg sync.WaitGroup + defer wg.Wait() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + log.Debug("configuring log-go-primary") + node, err := setupPrimaryFromFlags() + if err != nil { + log.Fatal("setup primary: %v", err) + } + + log.Debug("starting primary state manager routine") + go func() { + wg.Add(1) + defer wg.Done() + node.Stateman.Run(ctx) + log.Debug("state manager shutdown") + cancel() // must have state manager running + }() + + server := &http.Server{Addr: *externalEndpoint, Handler: node.PublicHTTPMux} + intserver := &http.Server{Addr: *internalEndpoint, Handler: node.InternalHTTPMux} + log.Debug("starting await routine") + go await(ctx, func() { + wg.Add(1) + defer wg.Done() + ctxInner, _ := context.WithTimeout(ctx, time.Second*60) + log.Info("stopping http server, please wait...") + server.Shutdown(ctxInner) + log.Info("... done") + log.Info("stopping internal api server, please wait...") + intserver.Shutdown(ctxInner) + log.Info("... done") + log.Info("stopping go routines, please wait...") + cancel() + log.Info("... done") + }) + + go func() { + wg.Add(1) + defer wg.Done() + log.Info("serving log nodes on %v/%v", *internalEndpoint, *prefix) + if err = intserver.ListenAndServe(); err != http.ErrServerClosed { + log.Error("serve(intserver): %v", err) + } + log.Debug("internal endpoints server shut down") + cancel() + }() + + log.Info("serving clients on %v/%v", *externalEndpoint, *prefix) + if err = server.ListenAndServe(); err != http.ErrServerClosed { + log.Error("serve(server): %v", err) + } + +} + +// setupPrimaryFromFlags() sets up a new sigsum primary node from flags. +func setupPrimaryFromFlags() (*primary.Primary, error) { + var p primary.Primary + var err error + + // Setup logging configuration. + p.Signer, p.Config.LogID, err = utils.NewLogIdentity(*key) + if err != nil { + return nil, fmt.Errorf("newLogIdentity: %v", err) + } + + p.Config.TreeID = *trillianID + p.Config.Prefix = *prefix + p.Config.MaxRange = *maxRange + p.Config.Deadline = *deadline + p.Config.Interval = *interval + p.Config.ShardStart = uint64(*shardStart) + if *shardStart < 0 { + return nil, fmt.Errorf("shard start must be larger than zero") + } + p.Config.Witnesses, err = newWitnessMap(*witnesses) + if err != nil { + return nil, fmt.Errorf("newWitnessMap: %v", err) + } + + // Setup trillian client. + dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(p.Config.Deadline)} + conn, err := grpc.Dial(*rpcBackend, dialOpts...) + if err != nil { + return nil, fmt.Errorf("Dial: %v", err) + } + p.TrillianClient = &db.TrillianClient{ + TreeID: p.TreeID, + GRPC: trillian.NewTrillianLogClient(conn), + } + + // Setup secondary node configuration. + if *secondaryURL != "" && *secondaryPubkey != "" { + pubkey, err := utils.PubkeyFromHexString(*secondaryPubkey) + if err != nil { + return nil, fmt.Errorf("invalid secondary node pubkey: %v", err) + } + p.Secondary = client.New(client.Config{ + LogURL: *secondaryURL, + LogPub: *pubkey, + }) + } else { + p.Secondary = client.New(client.Config{}) + } + + // Setup state manager. + p.Stateman, err = state.NewStateManagerSingle(p.TrillianClient, p.Signer, p.Config.Interval, p.Config.Deadline, p.Secondary) + if err != nil { + return nil, fmt.Errorf("NewStateManagerSingle: %v", err) + } + if *testMode == false { + p.DNS = dns.NewDefaultResolver() + } else { + p.DNS = dns.NewDummyResolver() + } + + // TODO: verify that GRPC.TreeType() == LOG. + + // Register HTTP endpoints. + mux := http.NewServeMux() + for _, h := range p.PublicHTTPHandlers() { + log.Debug("adding external handler: %s", h.Path()) + mux.Handle(h.Path(), h) + } + p.PublicHTTPMux = mux + + mux = http.NewServeMux() + for _, h := range p.InternalHTTPHandlers() { + log.Debug("adding internal handler: %s", h.Path()) + mux.Handle(h.Path(), h) + } + p.InternalHTTPMux = mux + + log.Debug("adding prometheus handler to internal mux, on path: /metrics") + http.Handle("/metrics", promhttp.Handler()) + + return &p, nil +} + +// newWitnessMap creates a new map of trusted witnesses +func newWitnessMap(witnesses string) (map[merkle.Hash]types.PublicKey, error) { + w := make(map[merkle.Hash]types.PublicKey) + if len(witnesses) > 0 { + for _, witness := range strings.Split(witnesses, ",") { + b, err := hex.DecodeString(witness) + if err != nil { + return nil, fmt.Errorf("DecodeString: %v", err) + } + + var vk types.PublicKey + if n := copy(vk[:], b); n != types.PublicKeySize { + return nil, fmt.Errorf("Invalid public key size: %v", n) + } + w[*merkle.HashFn(vk[:])] = vk + } + } + return w, nil +} + +// await waits for a shutdown signal and then runs a clean-up function +func await(ctx context.Context, done func()) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + select { + case <-sigs: + case <-ctx.Done(): + } + log.Debug("received shutdown signal") + done() +} diff --git a/cmd/sigsum-log-secondary/main.go b/cmd/sigsum-log-secondary/main.go new file mode 100644 index 0000000..7306d4c --- /dev/null +++ b/cmd/sigsum-log-secondary/main.go @@ -0,0 +1,175 @@ +// Package main provides a sigsum-log-secondary binary +package main + +import ( + "context" + "flag" + "fmt" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/google/trillian" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + + "git.sigsum.org/log-go/internal/db" + "git.sigsum.org/log-go/internal/node/secondary" + "git.sigsum.org/log-go/internal/utils" + "git.sigsum.org/sigsum-go/pkg/client" + "git.sigsum.org/sigsum-go/pkg/log" +) + +var ( + externalEndpoint = flag.String("external-endpoint", "localhost:6965", "host:port specification of where sigsum-log-secondary serves clients") + internalEndpoint = flag.String("internal-endpoint", "localhost:6967", "host:port specification of where sigsum-log-secondary serves other log nodes") + rpcBackend = flag.String("trillian-rpc-server", "localhost:6962", "host:port specification of where Trillian serves clients") + prefix = flag.String("url-prefix", "", "a prefix that proceeds /sigsum/v0/<endpoint>") + trillianID = flag.Int64("tree-id", 0, "log identifier in the Trillian database") + deadline = flag.Duration("deadline", time.Second*10, "deadline for backend requests") + key = flag.String("key", "", "path to file with hex-encoded Ed25519 private key") + interval = flag.Duration("interval", time.Second*30, "interval used to rotate the node's STH") + testMode = flag.Bool("test-mode", false, "run in test mode (Default: false)") + logFile = flag.String("log-file", "", "file to write logs to (Default: stderr)") + logLevel = flag.String("log-level", "info", "log level (Available options: debug, info, warning, error. Default: info)") + logColor = flag.Bool("log-color", false, "colored logging output (Default: false)") + primaryURL = flag.String("primary-url", "", "primary node endpoint for fetching leaves") + primaryPubkey = flag.String("primary-pubkey", "", "hex-encoded Ed25519 public key for primary node") + + gitCommit = "unknown" +) + +func main() { + flag.Parse() + + if err := utils.SetupLogging(*logFile, *logLevel, *logColor); err != nil { + log.Fatal("setup logging: %v", err) + } + log.Info("log-go git-commit %s", gitCommit) + + // wait for clean-up before exit + var wg sync.WaitGroup + defer wg.Wait() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + log.Debug("configuring log-go-secondary") + node, err := setupSecondaryFromFlags() + if err != nil { + log.Fatal("setup secondary: %v", err) + } + + log.Debug("starting periodic routine") + go func() { + wg.Add(1) + defer wg.Done() + node.Run(ctx) + log.Debug("periodic routine shutdown") + cancel() // must have periodic running + }() + + server := &http.Server{Addr: *externalEndpoint, Handler: node.PublicHTTPMux} + intserver := &http.Server{Addr: *internalEndpoint, Handler: node.InternalHTTPMux} + log.Debug("starting await routine") + go await(ctx, func() { + wg.Add(1) + defer wg.Done() + ctxInner, _ := context.WithTimeout(ctx, time.Second*60) + log.Info("stopping http server, please wait...") + server.Shutdown(ctxInner) + log.Info("... done") + log.Info("stopping internal api server, please wait...") + intserver.Shutdown(ctxInner) + log.Info("... done") + log.Info("stopping go routines, please wait...") + cancel() + log.Info("... done") + }) + + go func() { + wg.Add(1) + defer wg.Done() + log.Info("serving log nodes on %v/%v", *internalEndpoint, *prefix) + if err = intserver.ListenAndServe(); err != http.ErrServerClosed { + log.Error("serve(intserver): %v", err) + } + log.Debug("internal endpoints server shut down") + cancel() + }() + + log.Info("serving clients on %v/%v", *externalEndpoint, *prefix) + if err = server.ListenAndServe(); err != http.ErrServerClosed { + log.Error("serve(server): %v", err) + } + +} + +// setupSecondaryFromFlags() sets up a new sigsum secondary node from flags. +func setupSecondaryFromFlags() (*secondary.Secondary, error) { + var s secondary.Secondary + var err error + + // Setup logging configuration. + s.Signer, s.Config.LogID, err = utils.NewLogIdentity(*key) + if err != nil { + return nil, fmt.Errorf("newLogIdentity: %v", err) + } + s.Config.TreeID = *trillianID + s.Config.Prefix = *prefix + s.Config.Deadline = *deadline + s.Config.Interval = *interval + + // Setup trillian client. + dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(s.Config.Deadline)} + conn, err := grpc.Dial(*rpcBackend, dialOpts...) + if err != nil { + return nil, fmt.Errorf("Dial: %v", err) + } + s.TrillianClient = &db.TrillianClient{ + TreeID: s.TreeID, + GRPC: trillian.NewTrillianLogClient(conn), + } + + // Setup primary node configuration. + pubkey, err := utils.PubkeyFromHexString(*primaryPubkey) + if err != nil { + return nil, fmt.Errorf("invalid primary node pubkey: %v", err) + } + s.Primary = client.New(client.Config{ + LogURL: *primaryURL, + LogPub: *pubkey, + }) + + // TODO: verify that GRPC.TreeType() == PREORDERED_LOG. + + // Register HTTP endpoints. + mux := http.NewServeMux() + s.PublicHTTPMux = mux // No external endpoints but we want to return 404. + + mux = http.NewServeMux() + for _, h := range s.InternalHTTPHandlers() { + log.Debug("adding internal handler: %s", h.Path()) + mux.Handle(h.Path(), h) + } + s.InternalHTTPMux = mux + + log.Debug("adding prometheus handler to internal mux, on path: /metrics") + http.Handle("/metrics", promhttp.Handler()) + + return &s, nil +} + +// await waits for a shutdown signal and then runs a clean-up function +func await(ctx context.Context, done func()) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + select { + case <-sigs: + case <-ctx.Done(): + } + log.Debug("received shutdown signal") + done() +} diff --git a/cmd/sigsum_log_go/.gitignore b/cmd/sigsum_log_go/.gitignore deleted file mode 100644 index 254defd..0000000 --- a/cmd/sigsum_log_go/.gitignore +++ /dev/null @@ -1 +0,0 @@ -server diff --git a/cmd/sigsum_log_go/main.go b/cmd/sigsum_log_go/main.go deleted file mode 100644 index c8e1692..0000000 --- a/cmd/sigsum_log_go/main.go +++ /dev/null @@ -1,223 +0,0 @@ -// Package main provides a sigsum-log-go binary -package main - -import ( - "context" - "crypto" - "crypto/ed25519" - "encoding/hex" - "flag" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/signal" - "strings" - "sync" - "syscall" - "time" - - "github.com/google/trillian" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - - "git.sigsum.org/sigsum-go/pkg/log" - "git.sigsum.org/sigsum-go/pkg/types" - "git.sigsum.org/sigsum-go/pkg/dns" - "git.sigsum.org/log-go/pkg/db" - "git.sigsum.org/log-go/pkg/instance" - "git.sigsum.org/log-go/pkg/state" -) - -var ( - httpEndpoint = flag.String("http_endpoint", "localhost:6965", "host:port specification of where sigsum-log-go serves clients") - rpcBackend = flag.String("log_rpc_server", "localhost:6962", "host:port specification of where Trillian serves clients") - prefix = flag.String("prefix", "", "a prefix that proceeds /sigsum/v0/<endpoint>") - trillianID = flag.Int64("trillian_id", 0, "log identifier in the Trillian database") - deadline = flag.Duration("deadline", time.Second*10, "deadline for backend requests") - key = flag.String("key", "", "path to file with hex-encoded Ed25519 private key") - witnesses = flag.String("witnesses", "", "comma-separated list of trusted witness public keys in hex") - maxRange = flag.Int64("max_range", 10, "maximum number of entries that can be retrived in a single request") - interval = flag.Duration("interval", time.Second*30, "interval used to rotate the log's cosigned STH") - shardStart = flag.Int64("shard_interval_start", 0, "start of shard interval since the UNIX epoch in seconds") - logFile = flag.String("log-file", "", "file to write logs to (Default: stderr)") - logLevel = flag.String("log-level", "info", "log level (Available options: debug, info, warning, error. Default: info)") - logColor = flag.Bool("log-color", false, "colored logging output (Default: off)") - - gitCommit = "unknown" -) - -func main() { - flag.Parse() - - if err := setupLogging(*logFile, *logLevel, *logColor); err != nil { - log.Fatal("setup logging: %v", err) - } - log.Info("log-go git-commit %s", gitCommit) - - // wait for clean-up before exit - var wg sync.WaitGroup - defer wg.Wait() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - log.Debug("configuring log-go instance") - instance, err := setupInstanceFromFlags() - if err != nil { - log.Fatal("setup instance: %v", err) - } - - log.Debug("starting state manager routine") - go func() { - wg.Add(1) - defer wg.Done() - instance.Stateman.Run(ctx) - log.Debug("state manager shutdown") - cancel() // must have state manager running - }() - - log.Debug("starting await routine") - server := http.Server{Addr: *httpEndpoint} - go await(ctx, func() { - wg.Add(1) - defer wg.Done() - ctxInner, _ := context.WithTimeout(ctx, time.Second*60) - log.Info("stopping http server, please wait...") - server.Shutdown(ctxInner) - log.Info("stopping go routines, please wait...") - cancel() - }) - - log.Info("serving on %v/%v", *httpEndpoint, *prefix) - if err = server.ListenAndServe(); err != http.ErrServerClosed { - log.Error("serve: %v", err) - } -} - -func setupLogging(logFile, logLevel string, logColor bool) error { - if len(logFile) != 0 { - f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - log.SetOutput(f) - } - - switch logLevel { - case "debug": - log.SetLevel(log.DebugLevel) - case "info": - log.SetLevel(log.InfoLevel) - case "warning": - log.SetLevel(log.WarningLevel) - case "error": - log.SetLevel(log.ErrorLevel) - default: - return fmt.Errorf("invalid logging level %s", logLevel) - } - - log.SetColor(logColor) - return nil -} - -// SetupInstance sets up a new sigsum-log-go instance from flags -func setupInstanceFromFlags() (*instance.Instance, error) { - var i instance.Instance - var err error - - // Setup log configuration - i.Signer, i.LogID, err = newLogIdentity(*key) - if err != nil { - return nil, fmt.Errorf("newLogIdentity: %v", err) - } - i.TreeID = *trillianID - i.Prefix = *prefix - i.MaxRange = *maxRange - i.Deadline = *deadline - i.Interval = *interval - i.ShardStart = uint64(*shardStart) - if *shardStart < 0 { - return nil, fmt.Errorf("shard start must be larger than zero") - } - i.Witnesses, err = newWitnessMap(*witnesses) - if err != nil { - return nil, fmt.Errorf("newWitnessMap: %v", err) - } - - // Setup log client - dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(i.Deadline)} - conn, err := grpc.Dial(*rpcBackend, dialOpts...) - if err != nil { - return nil, fmt.Errorf("Dial: %v", err) - } - i.Client = &db.TrillianClient{ - TreeID: i.TreeID, - GRPC: trillian.NewTrillianLogClient(conn), - } - - // Setup state manager - i.Stateman, err = state.NewStateManagerSingle(i.Client, i.Signer, i.Interval, i.Deadline) - if err != nil { - return nil, fmt.Errorf("NewStateManagerSingle: %v", err) - } - - // Setup DNS verifier - i.DNS = dns.NewDefaultResolver() - - // Register HTTP endpoints - mux := http.NewServeMux() - http.Handle("/", mux) - for _, handler := range i.Handlers() { - log.Debug("adding handler: %s", handler.Path()) - mux.Handle(handler.Path(), handler) - } - log.Debug("adding prometheus handler on path: /metrics") - http.Handle("/metrics", promhttp.Handler()) - - return &i, nil -} - -func newLogIdentity(keyFile string) (crypto.Signer, string, error) { - buf, err := ioutil.ReadFile(keyFile) - if err != nil { - return nil, "", err - } - if buf, err = hex.DecodeString(strings.TrimSpace(string(buf))); err != nil { - return nil, "", fmt.Errorf("DecodeString: %v", err) - } - sk := crypto.Signer(ed25519.NewKeyFromSeed(buf)) - vk := sk.Public().(ed25519.PublicKey) - return sk, hex.EncodeToString([]byte(vk[:])), nil -} - -// newWitnessMap creates a new map of trusted witnesses -func newWitnessMap(witnesses string) (map[types.Hash]types.PublicKey, error) { - w := make(map[types.Hash]types.PublicKey) - if len(witnesses) > 0 { - for _, witness := range strings.Split(witnesses, ",") { - b, err := hex.DecodeString(witness) - if err != nil { - return nil, fmt.Errorf("DecodeString: %v", err) - } - - var vk types.PublicKey - if n := copy(vk[:], b); n != types.PublicKeySize { - return nil, fmt.Errorf("Invalid public key size: %v", n) - } - w[*types.HashFn(vk[:])] = vk - } - } - return w, nil -} - -// await waits for a shutdown signal and then runs a clean-up function -func await(ctx context.Context, done func()) { - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - select { - case <-sigs: - case <-ctx.Done(): - } - log.Debug("received shutdown signal") - done() -} |