diff --git a/.gitignore b/.gitignore index 15ccd00ed0f77a816351c0e2d6b11d174a6855ac..a56d2523228d6ba8f6a6f0cb17e1d052eba159ef 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ profile.cov **/yarn-error.log ./test +./bor-debug-* diff --git a/Makefile b/Makefile index 8a9fbaa0a2e84666ce7155691ee41ae7423b0137..b9bc9db602e795019d00ec08467cca8942206086 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,9 @@ GO ?= latest GORUN = env GO111MODULE=on go run GOPATH = $(shell go env GOPATH) +protoc: + protoc --go_out=. --go-grpc_out=. ./command/server/proto/*.proto + bor: $(GORUN) build/ci.go install ./cmd/geth mkdir -p $(GOPATH)/bin/ diff --git a/command/debug.go b/command/debug.go new file mode 100644 index 0000000000000000000000000000000000000000..b18281105313002abf46b5221f35fa151f0efdb1 --- /dev/null +++ b/command/debug.go @@ -0,0 +1,242 @@ +package main + +// Based on https://github.com/hashicorp/nomad/blob/main/command/operator_debug.go + +import ( + "archive/tar" + "compress/gzip" + "context" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "os/signal" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/command/flagset" + "github.com/ethereum/go-ethereum/command/server/proto" +) + +type DebugCommand struct { + *Meta2 + + seconds uint64 + output string +} + +// Help implements the cli.Command interface +func (d *DebugCommand) Help() string { + return `Usage: bor debug + + Build an archive containing Bor pprof traces + + ` + d.Flags().Help() +} + +func (d *DebugCommand) Flags() *flagset.Flagset { + flags := d.NewFlagSet("debug") + + flags.Uint64Flag(&flagset.Uint64Flag{ + Name: "seconds", + Usage: "seconds to trace", + Value: &d.seconds, + Default: 5, + }) + flags.StringFlag(&flagset.StringFlag{ + Name: "output", + Value: &d.output, + Usage: "Output directory", + }) + + return flags +} + +// Synopsis implements the cli.Command interface +func (d *DebugCommand) Synopsis() string { + return "Build an archive containing Bor pprof traces" +} + +// Run implements the cli.Command interface +func (d *DebugCommand) Run(args []string) int { + flags := d.Flags() + if err := flags.Parse(args); err != nil { + d.UI.Error(err.Error()) + return 1 + } + + clt, err := d.BorConn() + if err != nil { + d.UI.Error(err.Error()) + return 1 + } + + stamped := "bor-debug-" + time.Now().UTC().Format("2006-01-02-150405Z") + + // Create the output directory + var tmp string + if d.output != "" { + // User specified output directory + tmp = filepath.Join(d.output, stamped) + _, err := os.Stat(tmp) + if !os.IsNotExist(err) { + d.UI.Error("Output directory already exists") + return 1 + } + } else { + // Generate temp directory + tmp, err = ioutil.TempDir(os.TempDir(), stamped) + if err != nil { + d.UI.Error(fmt.Sprintf("Error creating tmp directory: %s", err.Error())) + return 1 + } + defer os.RemoveAll(tmp) + } + + d.UI.Output("Starting debugger...") + d.UI.Output("") + + // ensure destine folder exists + if err := os.MkdirAll(tmp, os.ModePerm); err != nil { + d.UI.Error(fmt.Sprintf("failed to create parent directory: %v", err)) + return 1 + } + + pprofProfile := func(ctx context.Context, profile string, filename string) error { + req := &proto.PprofRequest{ + Seconds: int64(d.seconds), + } + switch profile { + case "cpu": + req.Type = proto.PprofRequest_CPU + case "trace": + req.Type = proto.PprofRequest_TRACE + default: + req.Type = proto.PprofRequest_LOOKUP + req.Profile = profile + } + resp, err := clt.Pprof(ctx, req) + if err != nil { + return err + } + // write file + raw, err := hex.DecodeString(resp.Payload) + if err != nil { + return err + } + if err := ioutil.WriteFile(filepath.Join(tmp, filename+".prof"), raw, 0755); err != nil { + return err + } + return nil + } + + ctx, cancelFn := context.WithCancel(context.Background()) + trapSignal(cancelFn) + + profiles := map[string]string{ + "heap": "heap", + "cpu": "cpu", + "trace": "trace", + } + for profile, filename := range profiles { + if err := pprofProfile(ctx, profile, filename); err != nil { + d.UI.Error(fmt.Sprintf("Error creating profile '%s': %v", profile, err)) + return 1 + } + } + + // Exit before archive if output directory was specified + if d.output != "" { + d.UI.Output(fmt.Sprintf("Created debug directory: %s", tmp)) + return 0 + } + + // Create archive tarball + archiveFile := stamped + ".tar.gz" + if err = tarCZF(archiveFile, tmp, stamped); err != nil { + d.UI.Error(fmt.Sprintf("Error creating archive: %s", err.Error())) + return 1 + } + + d.UI.Output(fmt.Sprintf("Created debug archive: %s", archiveFile)) + return 0 +} + +func trapSignal(cancel func()) { + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + + go func() { + <-sigCh + cancel() + }() +} + +func tarCZF(archive string, src, target string) error { + // ensure the src actually exists before trying to tar it + if _, err := os.Stat(src); err != nil { + return fmt.Errorf("unable to tar files - %v", err.Error()) + } + + // create the archive + fh, err := os.Create(archive) + if err != nil { + return err + } + defer fh.Close() + + zz := gzip.NewWriter(fh) + defer zz.Close() + + tw := tar.NewWriter(zz) + defer tw.Close() + + // tar + return filepath.Walk(src, func(file string, fi os.FileInfo, err error) error { + // return on any error + if err != nil { + return err + } + if !fi.Mode().IsRegular() { + return nil + } + + header, err := tar.FileInfoHeader(fi, fi.Name()) + if err != nil { + return err + } + + // remove leading path to the src, so files are relative to the archive + path := strings.ReplaceAll(file, src, "") + if target != "" { + path = filepath.Join([]string{target, path}...) + } + path = strings.TrimPrefix(path, string(filepath.Separator)) + + header.Name = path + + if err := tw.WriteHeader(header); err != nil { + return err + } + + // copy the file contents + f, err := os.Open(file) + if err != nil { + return err + } + + if _, err := io.Copy(tw, f); err != nil { + return err + } + + f.Close() + return nil + }) +} diff --git a/command/main.go b/command/main.go index 67b5a004af3223452b0812e0aeb3bc37cb0b9d11..c4b9f3887ac50f64db19cbb78b977f4b0ecc8482 100644 --- a/command/main.go +++ b/command/main.go @@ -7,9 +7,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/command/flagset" "github.com/ethereum/go-ethereum/command/server" + "github.com/ethereum/go-ethereum/command/server/proto" "github.com/ethereum/go-ethereum/node" "github.com/mitchellh/cli" "github.com/ryanuber/columnize" + "google.golang.org/grpc" ) func main() { @@ -40,6 +42,9 @@ func commands() map[string]cli.CommandFactory { ErrorWriter: os.Stderr, } + meta2 := &Meta2{ + UI: ui, + } meta := &Meta{ UI: ui, } @@ -54,6 +59,11 @@ func commands() map[string]cli.CommandFactory { UI: ui, }, nil }, + "debug": func() (cli.Command, error) { + return &DebugCommand{ + Meta2: meta2, + }, nil + }, "account": func() (cli.Command, error) { return &Account{ UI: ui, @@ -77,6 +87,40 @@ func commands() map[string]cli.CommandFactory { } } +type Meta2 struct { + UI cli.Ui + + addr string +} + +func (m *Meta2) NewFlagSet(n string) *flagset.Flagset { + f := flagset.NewFlagSet(n) + + f.StringFlag(&flagset.StringFlag{ + Name: "address", + Value: &m.addr, + Usage: "Address of the grpc endpoint", + Default: "127.0.0.1:3131", + }) + return f +} + +func (m *Meta2) Conn() (*grpc.ClientConn, error) { + conn, err := grpc.Dial(m.addr, grpc.WithInsecure()) + if err != nil { + return nil, fmt.Errorf("failed to connect to server: %v", err) + } + return conn, nil +} + +func (m *Meta2) BorConn() (proto.BorClient, error) { + conn, err := m.Conn() + if err != nil { + return nil, err + } + return proto.NewBorClient(conn), nil +} + // Meta is a helper utility for the commands type Meta struct { UI cli.Ui diff --git a/command/server/command.go b/command/server/command.go index 361be1ba097b96f6d7d85cf6e64445abdc65ab51..4b2374dc427480ba2e2b8efaa9319c41ce53c109 100644 --- a/command/server/command.go +++ b/command/server/command.go @@ -2,27 +2,11 @@ package server import ( "fmt" - "io" - "io/ioutil" "os" "os/signal" - "strings" "syscall" - "time" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/tracers" - "github.com/ethereum/go-ethereum/ethstats" - "github.com/ethereum/go-ethereum/graphql" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/metrics/influxdb" - "github.com/ethereum/go-ethereum/node" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" "github.com/mitchellh/cli" ) @@ -38,8 +22,7 @@ type Command struct { configFile []string - // bor node - node *node.Node + srv *Server } // Help implements the cli.Command interface @@ -82,182 +65,16 @@ func (c *Command) Run(args []string) int { } c.config = config - // start the logger - setupLogger(config.LogLevel) - - // load the chain genesis - if err := config.loadChain(); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - // create the node/stack - nodeCfg, err := config.buildNode() - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - stack, err := node.New(nodeCfg) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - c.node = stack - - // register the ethereum backend - ethCfg, err := config.buildEth() + srv, err := NewServer(config) if err != nil { c.UI.Error(err.Error()) return 1 } - backend, err := eth.New(stack, ethCfg) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - - log.Info("Heimdall setup", "url", ethCfg.HeimdallURL) - - // debug tracing is enabled by default - stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) - - // graphql is started from another place - if config.JsonRPC.Graphql.Enabled { - if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.Modules); err != nil { - c.UI.Error(fmt.Sprintf("Failed to register the GraphQL service: %v", err)) - return 1 - } - } - - // register ethash service - if config.Ethstats != "" { - if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), config.Ethstats); err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - - // setup account manager (only keystore) - var borKeystore *keystore.KeyStore - { - keydir := stack.KeyStoreDir() - n, p := keystore.StandardScryptN, keystore.StandardScryptP - if config.Accounts.UseLightweightKDF { - n, p = keystore.LightScryptN, keystore.LightScryptP - } - borKeystore = keystore.NewKeyStore(keydir, n, p) - stack.AccountManager().AddBackend(borKeystore) - } - - // unlock accounts if necessary - if len(config.Accounts.Unlock) != 0 { - if err := c.unlockAccounts(borKeystore); err != nil { - c.UI.Error(fmt.Sprintf("failed to unlock: %v", err)) - return 1 - } - } + c.srv = srv - // sealing (if enabled) - if config.Sealer.Enabled { - if err := backend.StartMining(1); err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - - if err := c.setupTelemetry(config.Telemetry); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - // start the node - if err := c.node.Start(); err != nil { - c.UI.Error(err.Error()) - return 1 - } return c.handleSignals() } -func (c *Command) unlockAccounts(borKeystore *keystore.KeyStore) error { - // If insecure account unlocking is not allowed if node's APIs are exposed to external. - if !c.node.Config().InsecureUnlockAllowed && c.node.Config().ExtRPCEnabled() { - return fmt.Errorf("account unlock with HTTP access is forbidden") - } - - // read passwords from file if possible - passwords := []string{} - if c.config.Accounts.PasswordFile != "" { - var err error - if passwords, err = readMultilineFile(c.config.Accounts.PasswordFile); err != nil { - return err - } - } - decodePassword := func(addr common.Address, index int) (string, error) { - if len(passwords) > 0 { - if index < len(passwords) { - return passwords[index], nil - } - return passwords[len(passwords)-1], nil - } - // ask for the password - return c.UI.AskSecret(fmt.Sprintf("Please give a password to unlock '%s'", addr.String())) - } - - for index, addrStr := range c.config.Accounts.Unlock { - if !common.IsHexAddress(addrStr) { - return fmt.Errorf("unlock value '%s' is not an address", addrStr) - } - acct := accounts.Account{Address: common.HexToAddress(addrStr)} - - password, err := decodePassword(acct.Address, index) - if err != nil { - return err - } - if err := borKeystore.Unlock(acct, password); err != nil { - return err - } - log.Info("Unlocked account", "address", acct.Address.Hex()) - } - return nil -} - -func (c *Command) setupTelemetry(config *TelemetryConfig) error { - metrics.Enabled = config.Enabled - metrics.EnabledExpensive = config.Expensive - - if !metrics.Enabled { - // metrics are disabled, do not set up any sink - return nil - } - - log.Info("Enabling metrics collection") - - // influxdb - if v1Enabled, v2Enabled := (config.InfluxDB.V1Enabled), (config.InfluxDB.V2Enabled); v1Enabled || v2Enabled { - if v1Enabled && v2Enabled { - return fmt.Errorf("both influx v1 and influx v2 cannot be enabled") - } - - cfg := config.InfluxDB - tags := cfg.Tags - endpoint := cfg.Endpoint - - if v1Enabled { - log.Info("Enabling metrics export to InfluxDB (v1)") - go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags) - } - if v2Enabled { - log.Info("Enabling metrics export to InfluxDB (v2)") - go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags) - } - } - - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) - - return nil -} - func (c *Command) handleSignals() int { signalCh := make(chan os.Signal, 4) signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) @@ -269,8 +86,7 @@ func (c *Command) handleSignals() int { gracefulCh := make(chan struct{}) go func() { - c.node.Close() - c.node.Wait() + c.srv.Stop() close(gracefulCh) }() @@ -284,35 +100,3 @@ func (c *Command) handleSignals() int { } return 1 } - -func setupLogger(logLevel string) { - output := io.Writer(os.Stderr) - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - if usecolor { - output = colorable.NewColorableStderr() - } - ostream := log.StreamHandler(output, log.TerminalFormat(usecolor)) - glogger := log.NewGlogHandler(ostream) - - // logging - lvl, err := log.LvlFromString(strings.ToLower(logLevel)) - if err == nil { - glogger.Verbosity(lvl) - } else { - glogger.Verbosity(log.LvlInfo) - } - log.Root().SetHandler(glogger) -} - -func readMultilineFile(path string) ([]string, error) { - text, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - lines := strings.Split(string(text), "\n") - // Sanitise DOS line endings. - for i := range lines { - lines[i] = strings.TrimRight(lines[i], "\r") - } - return lines, nil -} diff --git a/command/server/config.go b/command/server/config.go index 177983a70237f63371baed046f075e7446060548..d5fbb491100fc66edb3e7120601521377525520c 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -84,6 +84,9 @@ type Config struct { // Account has the validator account related settings Accounts *AccountsConfig `hcl:"accounts,block"` + + // GRPC has the grpc server related settings + GRPC *GRPCConfig } type P2PConfig struct { @@ -226,6 +229,11 @@ type JsonRPCConfig struct { Graphql *APIConfig `hcl:"graphql,block"` } +type GRPCConfig struct { + // Addr is the bind address for the grpc rpc server + Addr string +} + type APIConfig struct { // Enabled selects whether the api is enabled Enabled bool `hcl:"enabled,optional"` @@ -459,6 +467,9 @@ func DefaultConfig() *Config { AllowInsecureUnlock: false, UseLightweightKDF: false, }, + GRPC: &GRPCConfig{ + Addr: ":3131", + }, } } diff --git a/command/server/flags.go b/command/server/flags.go index 98ab599f2fcfca1e5cf9a27445b3c1806ae80ec1..bd7ac80de347dca8d490b7f376cbf627144d65c1 100644 --- a/command/server/flags.go +++ b/command/server/flags.go @@ -440,5 +440,12 @@ func (c *Command) Flags() *flagset.Flagset { Value: &c.cliConfig.Accounts.UseLightweightKDF, }) + // grpc + f.StringFlag(&flagset.StringFlag{ + Name: "grpc.addr", + Usage: "Address and port to bind the GRPC server", + Value: &c.cliConfig.GRPC.Addr, + }) + return f } diff --git a/command/server/pprof/pprof.go b/command/server/pprof/pprof.go new file mode 100644 index 0000000000000000000000000000000000000000..44034f3bb8ddb4ba4437ce1389775e775d241255 --- /dev/null +++ b/command/server/pprof/pprof.go @@ -0,0 +1,93 @@ +package pprof + +import ( + "bytes" + "context" + "fmt" + "runtime" + "runtime/pprof" + "runtime/trace" + "time" +) + +// Profile generates a pprof.Profile report for the given profile name. +func Profile(profile string, debug, gc int) ([]byte, map[string]string, error) { + p := pprof.Lookup(profile) + if p == nil { + return nil, nil, fmt.Errorf("profile '%s' not found", profile) + } + + if profile == "heap" && gc > 0 { + runtime.GC() + } + + var buf bytes.Buffer + if err := p.WriteTo(&buf, debug); err != nil { + return nil, nil, err + } + + headers := map[string]string{ + "X-Content-Type-Options": "nosniff", + } + if debug != 0 { + headers["Content-Type"] = "text/plain; charset=utf-8" + } else { + headers["Content-Type"] = "application/octet-stream" + headers["Content-Disposition"] = fmt.Sprintf(`attachment; filename="%s"`, profile) + } + return buf.Bytes(), headers, nil +} + +// CPUProfile generates a CPU Profile for a given duration +func CPUProfile(ctx context.Context, sec int) ([]byte, map[string]string, error) { + if sec <= 0 { + sec = 1 + } + + var buf bytes.Buffer + if err := pprof.StartCPUProfile(&buf); err != nil { + return nil, nil, err + } + + sleep(ctx, time.Duration(sec)*time.Second) + + pprof.StopCPUProfile() + + return buf.Bytes(), + map[string]string{ + "X-Content-Type-Options": "nosniff", + "Content-Type": "application/octet-stream", + "Content-Disposition": `attachment; filename="profile"`, + }, nil +} + +// Trace runs a trace profile for a given duration +func Trace(ctx context.Context, sec int) ([]byte, map[string]string, error) { + if sec <= 0 { + sec = 1 + } + + var buf bytes.Buffer + if err := trace.Start(&buf); err != nil { + return nil, nil, err + } + + sleep(ctx, time.Duration(sec)*time.Second) + + trace.Stop() + + return buf.Bytes(), + map[string]string{ + "X-Content-Type-Options": "nosniff", + "Content-Type": "application/octet-stream", + "Content-Disposition": `attachment; filename="trace"`, + }, nil +} + +func sleep(ctx context.Context, d time.Duration) { + // Sleep until duration is met or ctx is cancelled + select { + case <-time.After(d): + case <-ctx.Done(): + } +} diff --git a/command/server/proto/server.pb.go b/command/server/proto/server.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..87ace6509ae345ce07469b9495a45372a672c22f --- /dev/null +++ b/command/server/proto/server.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.0 +// source: command/server/proto/server.proto + +package proto + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type PprofRequest_Type int32 + +const ( + PprofRequest_LOOKUP PprofRequest_Type = 0 + PprofRequest_CPU PprofRequest_Type = 1 + PprofRequest_TRACE PprofRequest_Type = 2 +) + +// Enum value maps for PprofRequest_Type. +var ( + PprofRequest_Type_name = map[int32]string{ + 0: "LOOKUP", + 1: "CPU", + 2: "TRACE", + } + PprofRequest_Type_value = map[string]int32{ + "LOOKUP": 0, + "CPU": 1, + "TRACE": 2, + } +) + +func (x PprofRequest_Type) Enum() *PprofRequest_Type { + p := new(PprofRequest_Type) + *p = x + return p +} + +func (x PprofRequest_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PprofRequest_Type) Descriptor() protoreflect.EnumDescriptor { + return file_command_server_proto_server_proto_enumTypes[0].Descriptor() +} + +func (PprofRequest_Type) Type() protoreflect.EnumType { + return &file_command_server_proto_server_proto_enumTypes[0] +} + +func (x PprofRequest_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PprofRequest_Type.Descriptor instead. +func (PprofRequest_Type) EnumDescriptor() ([]byte, []int) { + return file_command_server_proto_server_proto_rawDescGZIP(), []int{0, 0} +} + +type PprofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type PprofRequest_Type `protobuf:"varint,1,opt,name=type,proto3,enum=proto.PprofRequest_Type" json:"type,omitempty"` + Profile string `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` + Seconds int64 `protobuf:"varint,3,opt,name=seconds,proto3" json:"seconds,omitempty"` +} + +func (x *PprofRequest) Reset() { + *x = PprofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_command_server_proto_server_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PprofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PprofRequest) ProtoMessage() {} + +func (x *PprofRequest) ProtoReflect() protoreflect.Message { + mi := &file_command_server_proto_server_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PprofRequest.ProtoReflect.Descriptor instead. +func (*PprofRequest) Descriptor() ([]byte, []int) { + return file_command_server_proto_server_proto_rawDescGZIP(), []int{0} +} + +func (x *PprofRequest) GetType() PprofRequest_Type { + if x != nil { + return x.Type + } + return PprofRequest_LOOKUP +} + +func (x *PprofRequest) GetProfile() string { + if x != nil { + return x.Profile + } + return "" +} + +func (x *PprofRequest) GetSeconds() int64 { + if x != nil { + return x.Seconds + } + return 0 +} + +type PprofResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Headers map[string]string `protobuf:"bytes,2,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PprofResponse) Reset() { + *x = PprofResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_command_server_proto_server_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PprofResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PprofResponse) ProtoMessage() {} + +func (x *PprofResponse) ProtoReflect() protoreflect.Message { + mi := &file_command_server_proto_server_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PprofResponse.ProtoReflect.Descriptor instead. +func (*PprofResponse) Descriptor() ([]byte, []int) { + return file_command_server_proto_server_proto_rawDescGZIP(), []int{1} +} + +func (x *PprofResponse) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +func (x *PprofResponse) GetHeaders() map[string]string { + if x != nil { + return x.Headers + } + return nil +} + +var File_command_server_proto_server_proto protoreflect.FileDescriptor + +var file_command_server_proto_server_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0c, 0x50, + 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x26, 0x0a, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x10, + 0x00, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x50, 0x55, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, + 0x41, 0x43, 0x45, 0x10, 0x02, 0x22, 0xa2, 0x01, 0x0a, 0x0d, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x12, 0x3b, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, + 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x39, 0x0a, 0x03, 0x42, 0x6f, + 0x72, 0x12, 0x32, 0x0a, 0x05, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x17, 0x5a, 0x15, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_command_server_proto_server_proto_rawDescOnce sync.Once + file_command_server_proto_server_proto_rawDescData = file_command_server_proto_server_proto_rawDesc +) + +func file_command_server_proto_server_proto_rawDescGZIP() []byte { + file_command_server_proto_server_proto_rawDescOnce.Do(func() { + file_command_server_proto_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_command_server_proto_server_proto_rawDescData) + }) + return file_command_server_proto_server_proto_rawDescData +} + +var file_command_server_proto_server_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_command_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_command_server_proto_server_proto_goTypes = []interface{}{ + (PprofRequest_Type)(0), // 0: proto.PprofRequest.Type + (*PprofRequest)(nil), // 1: proto.PprofRequest + (*PprofResponse)(nil), // 2: proto.PprofResponse + nil, // 3: proto.PprofResponse.HeadersEntry +} +var file_command_server_proto_server_proto_depIdxs = []int32{ + 0, // 0: proto.PprofRequest.type:type_name -> proto.PprofRequest.Type + 3, // 1: proto.PprofResponse.headers:type_name -> proto.PprofResponse.HeadersEntry + 1, // 2: proto.Bor.Pprof:input_type -> proto.PprofRequest + 2, // 3: proto.Bor.Pprof:output_type -> proto.PprofResponse + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_command_server_proto_server_proto_init() } +func file_command_server_proto_server_proto_init() { + if File_command_server_proto_server_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_command_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PprofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_command_server_proto_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PprofResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_command_server_proto_server_proto_rawDesc, + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_command_server_proto_server_proto_goTypes, + DependencyIndexes: file_command_server_proto_server_proto_depIdxs, + EnumInfos: file_command_server_proto_server_proto_enumTypes, + MessageInfos: file_command_server_proto_server_proto_msgTypes, + }.Build() + File_command_server_proto_server_proto = out.File + file_command_server_proto_server_proto_rawDesc = nil + file_command_server_proto_server_proto_goTypes = nil + file_command_server_proto_server_proto_depIdxs = nil +} diff --git a/command/server/proto/server.proto b/command/server/proto/server.proto new file mode 100644 index 0000000000000000000000000000000000000000..2348e6dd6ae3b65fc611e7ab1455e411c885bfc9 --- /dev/null +++ b/command/server/proto/server.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package proto; + +option go_package = "/command/server/proto"; + +service Bor { + rpc Pprof(PprofRequest) returns (PprofResponse); +} + +message PprofRequest { + Type type = 1; + + string profile = 2; + + int64 seconds = 3; + + enum Type { + LOOKUP = 0; + CPU = 1; + TRACE = 2; + } +} + +message PprofResponse { + string payload = 1; + map<string, string> headers = 2; +} diff --git a/command/server/proto/server_grpc.pb.go b/command/server/proto/server_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..1c101f5e85596005350b5d6aa3ff0cd0a2d04036 --- /dev/null +++ b/command/server/proto/server_grpc.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// BorClient is the client API for Bor service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BorClient interface { + Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error) +} + +type borClient struct { + cc grpc.ClientConnInterface +} + +func NewBorClient(cc grpc.ClientConnInterface) BorClient { + return &borClient{cc} +} + +func (c *borClient) Pprof(ctx context.Context, in *PprofRequest, opts ...grpc.CallOption) (*PprofResponse, error) { + out := new(PprofResponse) + err := c.cc.Invoke(ctx, "/proto.Bor/Pprof", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BorServer is the server API for Bor service. +// All implementations must embed UnimplementedBorServer +// for forward compatibility +type BorServer interface { + Pprof(context.Context, *PprofRequest) (*PprofResponse, error) + mustEmbedUnimplementedBorServer() +} + +// UnimplementedBorServer must be embedded to have forward compatible implementations. +type UnimplementedBorServer struct { +} + +func (UnimplementedBorServer) Pprof(context.Context, *PprofRequest) (*PprofResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Pprof not implemented") +} +func (UnimplementedBorServer) mustEmbedUnimplementedBorServer() {} + +// UnsafeBorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BorServer will +// result in compilation errors. +type UnsafeBorServer interface { + mustEmbedUnimplementedBorServer() +} + +func RegisterBorServer(s grpc.ServiceRegistrar, srv BorServer) { + s.RegisterService(&Bor_ServiceDesc, srv) +} + +func _Bor_Pprof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PprofRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BorServer).Pprof(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.Bor/Pprof", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BorServer).Pprof(ctx, req.(*PprofRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Bor_ServiceDesc is the grpc.ServiceDesc for Bor service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Bor_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.Bor", + HandlerType: (*BorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Pprof", + Handler: _Bor_Pprof_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "command/server/proto/server.proto", +} diff --git a/command/server/server.go b/command/server/server.go new file mode 100644 index 0000000000000000000000000000000000000000..437a39b90631d10404531c763a4f5cc96be16598 --- /dev/null +++ b/command/server/server.go @@ -0,0 +1,202 @@ +package server + +import ( + "context" + "fmt" + "io" + "net" + "os" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/command/server/proto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/graphql" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/metrics/influxdb" + "github.com/ethereum/go-ethereum/node" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + "google.golang.org/grpc" +) + +type Server struct { + proto.UnimplementedBorServer + node *node.Node + grpcServer *grpc.Server +} + +func NewServer(config *Config) (*Server, error) { + srv := &Server{} + + // start the logger + setupLogger(config.LogLevel) + + if err := srv.setupGRPCServer(config.GRPC.Addr); err != nil { + return nil, err + } + + // load the chain genesis + if err := config.loadChain(); err != nil { + return nil, err + } + + // create the node/stack + nodeCfg, err := config.buildNode() + if err != nil { + return nil, err + } + stack, err := node.New(nodeCfg) + if err != nil { + return nil, err + } + srv.node = stack + + // register the ethereum backend + ethCfg, err := config.buildEth() + if err != nil { + return nil, err + } + backend, err := eth.New(stack, ethCfg) + if err != nil { + return nil, err + } + + // debug tracing is enabled by default + stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) + + // graphql is started from another place + if config.JsonRPC.Graphql.Enabled { + if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.Modules); err != nil { + return nil, fmt.Errorf("failed to register the GraphQL service: %v", err) + } + } + + // register ethash service + if config.Ethstats != "" { + if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), config.Ethstats); err != nil { + return nil, err + } + } + + // setup account manager (only keystore) + { + keydir := stack.KeyStoreDir() + n, p := keystore.StandardScryptN, keystore.StandardScryptP + if config.Accounts.UseLightweightKDF { + n, p = keystore.LightScryptN, keystore.LightScryptP + } + stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) + } + + // sealing (if enabled) + if config.Sealer.Enabled { + if err := backend.StartMining(1); err != nil { + return nil, err + } + } + + if err := srv.setupMetrics(config.Telemetry); err != nil { + return nil, err + } + + // start the node + if err := srv.node.Start(); err != nil { + return nil, err + } + return srv, nil +} + +func (s *Server) Stop() { + s.node.Close() +} + +func (s *Server) setupMetrics(config *TelemetryConfig) error { + metrics.Enabled = config.Enabled + metrics.EnabledExpensive = config.Expensive + + if !metrics.Enabled { + // metrics are disabled, do not set up any sink + return nil + } + + log.Info("Enabling metrics collection") + + // influxdb + if v1Enabled, v2Enabled := config.InfluxDB.V1Enabled, config.InfluxDB.V2Enabled; v1Enabled || v2Enabled { + if v1Enabled && v2Enabled { + return fmt.Errorf("both influx v1 and influx v2 cannot be enabled") + } + + cfg := config.InfluxDB + tags := cfg.Tags + endpoint := cfg.Endpoint + + if v1Enabled { + log.Info("Enabling metrics export to InfluxDB (v1)") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags) + } + if v2Enabled { + log.Info("Enabling metrics export to InfluxDB (v2)") + go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags) + } + } + + // Start system runtime metrics collection + go metrics.CollectProcessMetrics(3 * time.Second) + + return nil +} + +func (s *Server) setupGRPCServer(addr string) error { + s.grpcServer = grpc.NewServer(s.withLoggingUnaryInterceptor()) + proto.RegisterBorServer(s.grpcServer, s) + + lis, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + go func() { + if err := s.grpcServer.Serve(lis); err != nil { + log.Error("failed to serve grpc server", "err", err) + } + }() + + log.Info("GRPC Server started", "addr", addr) + return nil +} + +func (s *Server) withLoggingUnaryInterceptor() grpc.ServerOption { + return grpc.UnaryInterceptor(s.loggingServerInterceptor) +} + +func (s *Server) loggingServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + start := time.Now() + h, err := handler(ctx, req) + log.Trace("Request", "method", info.FullMethod, "duration", time.Since(start), "error", err) + return h, err +} + +func setupLogger(logLevel string) { + output := io.Writer(os.Stderr) + usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" + if usecolor { + output = colorable.NewColorableStderr() + } + ostream := log.StreamHandler(output, log.TerminalFormat(usecolor)) + glogger := log.NewGlogHandler(ostream) + + // logging + lvl, err := log.LvlFromString(strings.ToLower(logLevel)) + if err == nil { + glogger.Verbosity(lvl) + } else { + glogger.Verbosity(log.LvlInfo) + } + log.Root().SetHandler(glogger) +} diff --git a/command/server/service.go b/command/server/service.go new file mode 100644 index 0000000000000000000000000000000000000000..86ab38d5e2db8bd20510b244afa26bd1d6a7e9ef --- /dev/null +++ b/command/server/service.go @@ -0,0 +1,33 @@ +package server + +import ( + "context" + "encoding/hex" + + "github.com/ethereum/go-ethereum/command/server/pprof" + "github.com/ethereum/go-ethereum/command/server/proto" +) + +func (s *Server) Pprof(ctx context.Context, req *proto.PprofRequest) (*proto.PprofResponse, error) { + var payload []byte + var headers map[string]string + var err error + + switch req.Type { + case proto.PprofRequest_CPU: + payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds)) + case proto.PprofRequest_TRACE: + payload, headers, err = pprof.Trace(ctx, int(req.Seconds)) + case proto.PprofRequest_LOOKUP: + payload, headers, err = pprof.Profile(req.Profile, 0, 0) + } + if err != nil { + return nil, err + } + + resp := &proto.PprofResponse{ + Payload: hex.EncodeToString(payload), + Headers: headers, + } + return resp, nil +} diff --git a/go.mod b/go.mod index 7a2ed2478371402c9c6527fcee712f10a5d5d3ed..ee79f9b22223c6189eb78e577c830c3c42445542 100644 --- a/go.mod +++ b/go.mod @@ -74,6 +74,8 @@ require ( golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 golang.org/x/text v0.3.6 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba + google.golang.org/grpc v1.37.0 + google.golang.org/protobuf v1.25.0 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 diff --git a/go.sum b/go.sum index cdb8050f378ad40d826cb339511325f9cdf9234a..a8b1df8e13a526d6f7c987f70d043b94fc70baab 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= @@ -144,7 +145,9 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbT github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -201,6 +204,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= @@ -217,6 +221,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= @@ -672,18 +677,27 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=