From bc48b1b51d7916aa6b547f44d4edd79d83db7208 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 3 Dec 2021 01:02:32 +0100 Subject: [PATCH] node: add spy service Change-Id: Ieb04e6d26c7778d8a8afbbeaee79d764d9f2cd31 --- DEVELOP.md | 10 + Tiltfile | 25 +- buf.yaml | 5 + devnet/spy.yaml | 60 ++++ node/cmd/guardiand/adminserver.go | 27 +- node/cmd/guardiand/guardiankey.go | 5 +- node/cmd/guardiand/node.go | 43 +-- node/cmd/guardiand/publicrpc.go | 2 +- node/cmd/root.go | 2 + node/cmd/spy/spy.go | 322 ++++++++++++++++++ node/pkg/common/grpc.go | 30 ++ node/{cmd/guardiand => pkg/common}/nodekey.go | 17 +- node/pkg/common/sysutils.go | 25 ++ node/pkg/p2p/p2p.go | 5 + proto/gossip/v1/gossip.proto | 2 - proto/spy/v1/spy.proto | 44 +++ tools/build.sh | 1 + tools/go.mod | 99 ++++++ tools/go.sum | 18 +- tools/tools.go | 1 + 20 files changed, 662 insertions(+), 81 deletions(-) create mode 100644 devnet/spy.yaml create mode 100644 node/cmd/spy/spy.go create mode 100644 node/pkg/common/grpc.go rename node/{cmd/guardiand => pkg/common}/nodekey.go (72%) create mode 100644 node/pkg/common/sysutils.go create mode 100644 proto/spy/v1/spy.proto diff --git a/DEVELOP.md b/DEVELOP.md index 1dd7fcaa4..e4895088c 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -133,6 +133,16 @@ IntelliJ's [remote development backend](https://www.jetbrains.com/remote-develop ## Tips and tricks +### Call gRPC services + + tools/bin/grpcurl -protoset <(tools/bin/buf build -o -) -plaintext localhost:7072 spy.v1.SpyRPCService/SubscribeSignedVAA + +With parameters (using proto json encoding): + + tools/bin/grpcurl -protoset <(tools/bin/buf build -o -) \ + -d '{"filters": [{"emitter_filter": {"emitter_address": "574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d", "chain_id": "CHAIN_ID_SOLANA"}}]}' \ + -plaintext localhost:7072 spy.v1.SpyRPCService/SubscribeSignedVAA + ### Post messages To Solana: diff --git a/Tiltfile b/Tiltfile index 0741d72ae..953fcd61c 100644 --- a/Tiltfile +++ b/Tiltfile @@ -137,6 +137,14 @@ k8s_resource("guardian", resource_deps = ["proto-gen", "solana-devnet"], port_fo port_forward(2345, name = "Debugger [:2345]", host = webHost), ]) +# spy +k8s_yaml_with_ns("devnet/spy.yaml") + +k8s_resource("spy", resource_deps = ["proto-gen", "guardian"], port_forwards = [ + port_forward(6061, container_port = 6060, name = "Debug/Status Server [:6061]", host = webHost), + port_forward(7072, name = "Spy gRPC [:7072]", host = webHost), +]) + # solana client cli (used for devnet setup) docker_build( @@ -228,7 +236,6 @@ k8s_resource("eth-devnet2", port_forwards = [ ]) if bridge_ui: - docker_build( ref = "bridge-ui", context = ".", @@ -272,26 +279,27 @@ def build_cloud_function(container_name, go_func_name, path, builder): if ci: # inherit the DOCKER_HOST socket provided by custom_build. pack_build_cmd = pack_build_cmd + " --docker-host inherit" + # do not attempt to access Docker cache in CI # pack_build_cmd = pack_build_cmd + " --clear-cache" # don't try to pull previous container versions in CI pack_build_cmd = pack_build_cmd + " --pull-policy never" + # push to kubernetes registry disable_push = False skips_local_docker = False - docker_tag_cmd = "tilt docker -- tag " + caching_ref + " $EXPECTED_REF" + docker_tag_cmd = "tilt docker -- tag " + caching_ref + " $EXPECTED_REF" custom_build( container_name, pack_build_cmd + " && " + docker_tag_cmd, [path], - tag=tag, - skips_local_docker=skips_local_docker, - disable_push=disable_push, + tag = tag, + skips_local_docker = skips_local_docker, + disable_push = disable_push, ) if explorer: - local_resource( name = "devnet-cloud-function", cmd = "tilt docker -- build -f ./event_database/cloud_functions/Dockerfile.run . -t devnet-cloud-function --label builtby=tilt", @@ -308,7 +316,8 @@ if explorer: k8s_yaml_with_ns("devnet/bigtable.yaml") - k8s_resource("bigtable-emulator", + k8s_resource( + "bigtable-emulator", port_forwards = [port_forward(8086, name = "BigTable clients [:8086]", host = webHost)], labels = ["explorer"], ) @@ -323,7 +332,7 @@ if explorer: "bigtable-functions", resource_deps = ["proto-gen", "bigtable-emulator"], port_forwards = [port_forward(8090, name = "BigTable Functions [:8090]", host = webHost)], - labels = ["explorer"] + labels = ["explorer"], ) # explorer web app diff --git a/buf.yaml b/buf.yaml index 6f035b7e2..226555f9c 100644 --- a/buf.yaml +++ b/buf.yaml @@ -12,6 +12,11 @@ lint: - DEFAULT # https://github.com/twitchtv/twirp/issues/70#issuecomment-470367807 - UNARY_RPC + ignore_only: + RPC_NO_SERVER_STREAMING: + # Allow streamed RPC for the spy server, which is designed to run as a sidecar + # and won't handle large amounts of connections. + - spy/v1/spy.proto breaking: use: - WIRE_JSON diff --git a/devnet/spy.yaml b/devnet/spy.yaml new file mode 100644 index 000000000..3780b34e4 --- /dev/null +++ b/devnet/spy.yaml @@ -0,0 +1,60 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: spy + labels: + app: spy +spec: + ports: + - port: 7072 + name: spyrpc + protocol: TCP + - port: 6060 + name: status + protocol: TCP + clusterIP: None + selector: + app: spy +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: spy +spec: + selector: + matchLabels: + app: spy + serviceName: spy + replicas: 1 + template: + metadata: + labels: + app: spy + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: spy + image: guardiand-image + command: + - /guardiand + - spy + - --nodeKey + - /tmp/node.key + - --spyRPC + - "[::]:7072" + # Hardcoded devnet bootstrap (generated from deterministic key in guardiand) + - --bootstrap + - /dns4/guardian-0.guardian/udp/8999/quic/p2p/12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw +# - --logLevel=debug + ports: + - containerPort: 7072 + name: spyrpc + protocol: TCP + - containerPort: 6060 + name: status + protocol: TCP + readinessProbe: + httpGet: + port: 6060 + path: /metrics diff --git a/node/cmd/guardiand/adminserver.go b/node/cmd/guardiand/adminserver.go index 4e0a12a84..554ac5bc8 100644 --- a/node/cmd/guardiand/adminserver.go +++ b/node/cmd/guardiand/adminserver.go @@ -9,12 +9,7 @@ import ( publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1" "github.com/certusone/wormhole/node/pkg/publicrpc" ethcommon "github.com/ethereum/go-ethereum/common" - grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" - grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" - grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" - grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "go.uber.org/zap" - "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "math" @@ -269,28 +264,8 @@ func adminServiceRunnable(logger *zap.Logger, socketPath string, injectC chan<- publicrpcService := publicrpc.NewPublicrpcServer(logger, db, gst) - grpcServer := newGRPCServer(logger) + grpcServer := common.NewInstrumentedGRPCServer(logger) nodev1.RegisterNodePrivilegedServiceServer(grpcServer, nodeService) publicrpcv1.RegisterPublicRPCServiceServer(grpcServer, publicrpcService) return supervisor.GRPCServer(grpcServer, l, false), nil } - -func newGRPCServer(logger *zap.Logger) *grpc.Server { - server := grpc.NewServer( - grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( - grpc_ctxtags.StreamServerInterceptor(), - grpc_prometheus.StreamServerInterceptor, - grpc_zap.StreamServerInterceptor(logger), - )), - grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( - grpc_ctxtags.UnaryServerInterceptor(), - grpc_prometheus.UnaryServerInterceptor, - grpc_zap.UnaryServerInterceptor(logger), - )), - ) - - grpc_prometheus.EnableHandlingTimeHistogram() - grpc_prometheus.Register(server) - - return server -} diff --git a/node/cmd/guardiand/guardiankey.go b/node/cmd/guardiand/guardiankey.go index 500991d4a..f63e6ed57 100644 --- a/node/cmd/guardiand/guardiankey.go +++ b/node/cmd/guardiand/guardiankey.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "errors" "fmt" + "github.com/certusone/wormhole/node/pkg/common" "io/ioutil" "log" "os" @@ -36,8 +37,8 @@ var KeygenCmd = &cobra.Command{ } func runKeygen(cmd *cobra.Command, args []string) { - lockMemory() - setRestrictiveUmask() + common.LockMemory() + common.SetRestrictiveUmask() log.Print("Creating new key at ", args[0]) diff --git a/node/cmd/guardiand/node.go b/node/cmd/guardiand/node.go index ccd14d11a..06530cbca 100644 --- a/node/cmd/guardiand/node.go +++ b/node/cmd/guardiand/node.go @@ -3,30 +3,20 @@ package guardiand import ( "context" "fmt" + "github.com/certusone/wormhole/node/pkg/db" "github.com/certusone/wormhole/node/pkg/notify/discord" + "github.com/gagliardetto/solana-go/rpc" "log" "net/http" _ "net/http/pprof" "os" "path" "strings" - "syscall" - - "github.com/certusone/wormhole/node/pkg/db" - "github.com/gagliardetto/solana-go/rpc" solana_types "github.com/gagliardetto/solana-go" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus/promhttp" - eth_common "github.com/ethereum/go-ethereum/common" - ethcrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/spf13/cobra" - "go.uber.org/zap" - "golang.org/x/sys/unix" - "github.com/certusone/wormhole/node/pkg/common" "github.com/certusone/wormhole/node/pkg/devnet" "github.com/certusone/wormhole/node/pkg/ethereum" @@ -38,6 +28,12 @@ import ( solana "github.com/certusone/wormhole/node/pkg/solana" "github.com/certusone/wormhole/node/pkg/supervisor" "github.com/certusone/wormhole/node/pkg/vaa" + eth_common "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/spf13/cobra" + "go.uber.org/zap" "github.com/certusone/wormhole/node/pkg/terra" @@ -199,23 +195,6 @@ func rootLoggerName() string { } } -// lockMemory locks current and future pages in memory to protect secret keys from being swapped out to disk. -// It's possible (and strongly recommended) to deploy Wormhole such that keys are only ever -// stored in memory and never touch the disk. This is a privileged operation and requires CAP_IPC_LOCK. -func lockMemory() { - err := unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) - if err != nil { - fmt.Printf("Failed to lock memory: %v (CAP_IPC_LOCK missing?)\n", err) - os.Exit(1) - } -} - -// setRestrictiveUmask masks the group and world bits. This ensures that key material -// and sockets we create aren't accidentally group- or world-readable. -func setRestrictiveUmask() { - syscall.Umask(0077) // cannot fail -} - // NodeCmd represents the node command var NodeCmd = &cobra.Command{ Use: "node", @@ -228,8 +207,8 @@ func runNode(cmd *cobra.Command, args []string) { fmt.Print(devwarning) } - lockMemory() - setRestrictiveUmask() + common.LockMemory() + common.SetRestrictiveUmask() // Refuse to run as root in production mode. if !*unsafeDevMode && os.Geteuid() == 0 { @@ -506,7 +485,7 @@ func runNode(cmd *cobra.Command, args []string) { } priv = devnet.DeterministicP2PPrivKeyByIndex(int64(idx)) } else { - priv, err = getOrCreateNodeKey(logger, *nodeKeyPath) + priv, err = common.GetOrCreateNodeKey(logger, *nodeKeyPath) if err != nil { logger.Fatal("Failed to load node key", zap.Error(err)) } diff --git a/node/cmd/guardiand/publicrpc.go b/node/cmd/guardiand/publicrpc.go index f6f11776d..4cb541509 100644 --- a/node/cmd/guardiand/publicrpc.go +++ b/node/cmd/guardiand/publicrpc.go @@ -21,7 +21,7 @@ func publicrpcServiceRunnable(logger *zap.Logger, listenAddr string, db *db.Data logger.Info("publicrpc server listening", zap.String("addr", l.Addr().String())) rpcServer := publicrpc.NewPublicrpcServer(logger, db, gst) - grpcServer := newGRPCServer(logger) + grpcServer := common.NewInstrumentedGRPCServer(logger) publicrpcv1.RegisterPublicRPCServiceServer(grpcServer, rpcServer) return supervisor.GRPCServer(grpcServer, l, false), grpcServer, nil diff --git a/node/cmd/root.go b/node/cmd/root.go index c2d4c853c..3a5cbe3dd 100644 --- a/node/cmd/root.go +++ b/node/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/certusone/wormhole/node/cmd/debug" + "github.com/certusone/wormhole/node/cmd/spy" "github.com/certusone/wormhole/node/pkg/version" "os" @@ -45,6 +46,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.guardiand.yaml)") rootCmd.AddCommand(guardiand.NodeCmd) + rootCmd.AddCommand(spy.SpyCmd) rootCmd.AddCommand(guardiand.KeygenCmd) rootCmd.AddCommand(guardiand.AdminCmd) rootCmd.AddCommand(guardiand.TemplateCmd) diff --git a/node/cmd/spy/spy.go b/node/cmd/spy/spy.go new file mode 100644 index 000000000..3b315c264 --- /dev/null +++ b/node/cmd/spy/spy.go @@ -0,0 +1,322 @@ +package spy + +import ( + "context" + "encoding/hex" + "fmt" + "github.com/certusone/wormhole/node/pkg/common" + "github.com/certusone/wormhole/node/pkg/p2p" + gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1" + "github.com/certusone/wormhole/node/pkg/proto/spy/v1" + "github.com/certusone/wormhole/node/pkg/supervisor" + "github.com/certusone/wormhole/node/pkg/vaa" + "github.com/google/uuid" + "github.com/gorilla/mux" + ipfslog "github.com/ipfs/go-log/v2" + "github.com/libp2p/go-libp2p-core/crypto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/spf13/cobra" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net" + "net/http" + "os" + "sync" +) + +var ( + rootCtx context.Context + rootCtxCancel context.CancelFunc +) + +var ( + p2pNetworkID *string + p2pPort *uint + p2pBootstrap *string + + statusAddr *string + + nodeKeyPath *string + + logLevel *string + + spyRPC *string +) + +func init() { + p2pNetworkID = SpyCmd.Flags().String("network", "/wormhole/dev", "P2P network identifier") + p2pPort = SpyCmd.Flags().Uint("port", 8999, "P2P UDP listener port") + p2pBootstrap = SpyCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (comma-separated)") + + statusAddr = SpyCmd.Flags().String("statusAddr", "[::]:6060", "Listen address for status server (disabled if blank)") + + nodeKeyPath = SpyCmd.Flags().String("nodeKey", "", "Path to node key (will be generated if it doesn't exist)") + + logLevel = SpyCmd.Flags().String("logLevel", "info", "Logging level (debug, info, warn, error, dpanic, panic, fatal)") + + spyRPC = SpyCmd.Flags().String("spyRPC", "", "Listen address for gRPC interface") +} + +// SpyCmd represents the node command +var SpyCmd = &cobra.Command{ + Use: "spy", + Short: "Run gossip spy client", + Run: runSpy, +} + +type spyServer struct { + spyv1.UnimplementedSpyRPCServiceServer + logger *zap.Logger + subs map[string]*subscription + subsMu sync.Mutex +} + +type message struct { + vaaBytes []byte +} + +type filter struct { + chainId vaa.ChainID + emitterAddr vaa.Address +} + +type subscription struct { + filters []filter + ch chan message +} + +func subscriptionId() string { + return uuid.New().String() +} + +func decodeEmitterAddr(hexAddr string) (vaa.Address, error) { + address, err := hex.DecodeString(hexAddr) + if err != nil { + return vaa.Address{}, status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode address: %v", err)) + } + if len(address) != 32 { + return vaa.Address{}, status.Error(codes.InvalidArgument, "address must be 32 bytes") + } + + addr := vaa.Address{} + copy(addr[:], address) + + return addr, nil +} + +func (s *spyServer) Publish(vaaBytes []byte) error { + s.subsMu.Lock() + defer s.subsMu.Unlock() + + var v *vaa.VAA + + for _, sub := range s.subs { + if len(sub.filters) == 0 { + sub.ch <- message{vaaBytes: vaaBytes} + } else { + if v == nil { + var err error + v, err = vaa.Unmarshal(vaaBytes) + if err != nil { + return err + } + } + + for _, fi := range sub.filters { + if fi.chainId == v.EmitterChain && fi.emitterAddr == v.EmitterAddress { + sub.ch <- message{vaaBytes: vaaBytes} + } + } + } + } + + return nil +} + +func (s *spyServer) SubscribeSignedVAA(req *spyv1.SubscribeSignedVAARequest, resp spyv1.SpyRPCService_SubscribeSignedVAAServer) error { + var fi []filter + if req.Filters != nil { + for _, f := range req.Filters { + switch t := f.Filter.(type) { + case *spyv1.FilterEntry_EmitterFilter: + addr, err := decodeEmitterAddr(t.EmitterFilter.EmitterAddress) + if err != nil { + return status.Error(codes.InvalidArgument, fmt.Sprintf("failed to decode emitter address: %v", err)) + } + fi = append(fi, filter{ + chainId: vaa.ChainID(t.EmitterFilter.ChainId), + emitterAddr: addr, + }) + default: + return status.Error(codes.InvalidArgument, "unsupported filter type") + } + } + } + + s.subsMu.Lock() + id := subscriptionId() + sub := &subscription{ + ch: make(chan message, 1), + filters: fi, + } + s.subs[id] = sub + s.subsMu.Unlock() + + defer func() { + s.subsMu.Lock() + defer s.subsMu.Unlock() + delete(s.subs, id) + }() + + for { + select { + case <-resp.Context().Done(): + return resp.Context().Err() + case msg := <-sub.ch: + if err := resp.Send(&spyv1.SubscribeSignedVAAResponse{ + VaaBytes: msg.vaaBytes, + }); err != nil { + return err + } + } + } +} + +func newSpyServer(logger *zap.Logger) *spyServer { + return &spyServer{ + logger: logger.Named("spyserver"), + subs: make(map[string]*subscription), + } +} + +func spyServerRunnable(s *spyServer, logger *zap.Logger, listenAddr string) (supervisor.Runnable, *grpc.Server, error) { + l, err := net.Listen("tcp", listenAddr) + if err != nil { + return nil, nil, fmt.Errorf("failed to listen: %w", err) + } + + logger.Info("publicrpc server listening", zap.String("addr", l.Addr().String())) + + grpcServer := common.NewInstrumentedGRPCServer(logger) + spyv1.RegisterSpyRPCServiceServer(grpcServer, s) + + return supervisor.GRPCServer(grpcServer, l, false), grpcServer, nil +} + +func runSpy(cmd *cobra.Command, args []string) { + common.SetRestrictiveUmask() + + lvl, err := ipfslog.LevelFromString(*logLevel) + if err != nil { + fmt.Println("Invalid log level") + os.Exit(1) + } + + logger := ipfslog.Logger("wormhole-spy").Desugar() + + ipfslog.SetAllLoggers(lvl) + + // Status server + if *statusAddr != "" { + router := mux.NewRouter() + + router.Handle("/metrics", promhttp.Handler()) + + go func() { + logger.Info("status server listening on [::]:6060") + logger.Error("status server crashed", zap.Error(http.ListenAndServe(*statusAddr, router))) + }() + } + + // Verify flags + + if *nodeKeyPath == "" { + logger.Fatal("Please specify --nodeKey") + } + if *p2pBootstrap == "" { + logger.Fatal("Please specify --bootstrap") + } + + // Node's main lifecycle context. + rootCtx, rootCtxCancel = context.WithCancel(context.Background()) + defer rootCtxCancel() + + // Outbound gossip message queue + sendC := make(chan []byte) + + // Inbound observations + obsvC := make(chan *gossipv1.SignedObservation, 50) + + // Inbound signed VAAs + signedInC := make(chan *gossipv1.SignedVAAWithQuorum, 50) + + // Guardian set state managed by processor + gst := common.NewGuardianSetState() + + // RPC server + s := newSpyServer(logger) + rpcSvc, _, err := spyServerRunnable(s, logger, *spyRPC) + if err != nil { + logger.Fatal("failed to start RPC server", zap.Error(err)) + } + + // Ignore observations + go func() { + for { + select { + case <-rootCtx.Done(): + return + case <-obsvC: + } + } + }() + + // Log signed VAAs + go func() { + for { + select { + case <-rootCtx.Done(): + return + case v := <-signedInC: + logger.Info("Received signed VAA", + zap.Any("vaa", v.Vaa)) + if err := s.Publish(v.Vaa); err != nil { + logger.Error("failed to publish signed VAA", zap.Error(err)) + } + } + } + }() + + // Load p2p private key + var priv crypto.PrivKey + priv, err = common.GetOrCreateNodeKey(logger, *nodeKeyPath) + if err != nil { + logger.Fatal("Failed to load node key", zap.Error(err)) + } + + // Run supervisor. + supervisor.New(rootCtx, logger, func(ctx context.Context) error { + if err := supervisor.Run(ctx, "p2p", p2p.Run( + obsvC, sendC, signedInC, priv, nil, gst, *p2pPort, *p2pNetworkID, *p2pBootstrap, "", false, rootCtxCancel)); err != nil { + return err + } + + if err := supervisor.Run(ctx, "spyrpc", rpcSvc); err != nil { + return err + } + + logger.Info("Started internal services") + + <-ctx.Done() + return nil + }, + // It's safer to crash and restart the process in case we encounter a panic, + // rather than attempting to reschedule the runnable. + supervisor.WithPropagatePanic) + + <-rootCtx.Done() + logger.Info("root context cancelled, exiting...") + // TODO: wait for things to shut down gracefully +} diff --git a/node/pkg/common/grpc.go b/node/pkg/common/grpc.go new file mode 100644 index 000000000..cc8960994 --- /dev/null +++ b/node/pkg/common/grpc.go @@ -0,0 +1,30 @@ +package common + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" + "github.com/grpc-ecosystem/go-grpc-middleware/tags" + "github.com/grpc-ecosystem/go-grpc-prometheus" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +func NewInstrumentedGRPCServer(logger *zap.Logger) *grpc.Server { + server := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( + grpc_ctxtags.StreamServerInterceptor(), + grpc_prometheus.StreamServerInterceptor, + grpc_zap.StreamServerInterceptor(logger), + )), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_prometheus.UnaryServerInterceptor, + grpc_zap.UnaryServerInterceptor(logger), + )), + ) + + grpc_prometheus.EnableHandlingTimeHistogram() + grpc_prometheus.Register(server) + + return server +} diff --git a/node/cmd/guardiand/nodekey.go b/node/pkg/common/nodekey.go similarity index 72% rename from node/cmd/guardiand/nodekey.go rename to node/pkg/common/nodekey.go index 982270169..4485c4fff 100644 --- a/node/cmd/guardiand/nodekey.go +++ b/node/pkg/common/nodekey.go @@ -1,27 +1,26 @@ -package guardiand +package common import ( "fmt" - "io/ioutil" - "os" - - p2pcrypto "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/peer" "go.uber.org/zap" + "io/ioutil" + "os" ) -func getOrCreateNodeKey(logger *zap.Logger, path string) (p2pcrypto.PrivKey, error) { +func GetOrCreateNodeKey(logger *zap.Logger, path string) (crypto.PrivKey, error) { b, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { logger.Info("No node key found, generating a new one...", zap.String("path", path)) - priv, _, err := p2pcrypto.GenerateKeyPair(p2pcrypto.Ed25519, -1) + priv, _, err := crypto.GenerateKeyPair(crypto.Ed25519, -1) if err != nil { panic(err) } - s, err := p2pcrypto.MarshalPrivateKey(priv) + s, err := crypto.MarshalPrivateKey(priv) if err != nil { panic(err) } @@ -37,7 +36,7 @@ func getOrCreateNodeKey(logger *zap.Logger, path string) (p2pcrypto.PrivKey, err } } - priv, err := p2pcrypto.UnmarshalPrivateKey(b) + priv, err := crypto.UnmarshalPrivateKey(b) if err != nil { return nil, fmt.Errorf("failed to unmarshal node key: %w", err) } diff --git a/node/pkg/common/sysutils.go b/node/pkg/common/sysutils.go new file mode 100644 index 000000000..ed834742a --- /dev/null +++ b/node/pkg/common/sysutils.go @@ -0,0 +1,25 @@ +package common + +import ( + "fmt" + "golang.org/x/sys/unix" + "os" + "syscall" +) + +// LockMemory locks current and future pages in memory to protect secret keys from being swapped out to disk. +// It's possible (and strongly recommended) to deploy Wormhole such that keys are only ever +// stored in memory and never touch the disk. This is a privileged operation and requires CAP_IPC_LOCK. +func LockMemory() { + err := unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) + if err != nil { + fmt.Printf("Failed to lock memory: %v (CAP_IPC_LOCK missing?)\n", err) + os.Exit(1) + } +} + +// SetRestrictiveUmask masks the group and world bits. This ensures that key material +// and sockets we create aren't accidentally group- or world-readable. +func SetRestrictiveUmask() { + syscall.Umask(0077) // cannot fail +} diff --git a/node/pkg/p2p/p2p.go b/node/pkg/p2p/p2p.go index ba2b1baac..073f29ebd 100644 --- a/node/pkg/p2p/p2p.go +++ b/node/pkg/p2p/p2p.go @@ -193,6 +193,11 @@ func Run(obsvC chan *gossipv1.SignedObservation, sendC chan []byte, signedInC ch }() go func() { + // Disable heartbeat when no node name is provided (spy mode) + if nodeName == "" { + return + } + ctr := int64(0) tick := time.NewTicker(15 * time.Second) defer tick.Stop() diff --git a/proto/gossip/v1/gossip.proto b/proto/gossip/v1/gossip.proto index 61281d7fc..32a715209 100644 --- a/proto/gossip/v1/gossip.proto +++ b/proto/gossip/v1/gossip.proto @@ -6,8 +6,6 @@ option go_package = "github.com/certusone/wormhole/node/pkg/proto/gossip/v1;goss message GossipMessage { oneof message { - // Deprecated: use SignedHeartbeat. - Heartbeat heartbeat = 1; SignedObservation signed_observation = 2; SignedHeartbeat signed_heartbeat = 3; SignedVAAWithQuorum signed_vaa_with_quorum = 4; diff --git a/proto/spy/v1/spy.proto b/proto/spy/v1/spy.proto new file mode 100644 index 000000000..e054892c1 --- /dev/null +++ b/proto/spy/v1/spy.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package spy.v1; + +option go_package = "github.com/certusone/wormhole/node/pkg/proto/spy/v1;spyv1"; + +import "google/api/annotations.proto"; +import "publicrpc/v1/publicrpc.proto"; + +// SpyRPCService exposes a gossip introspection service, allowing sniffing of gossip messages. +service SpyRPCService { + // SubscribeSignedVAA returns a stream of signed VAA messages received on the network. + rpc SubscribeSignedVAA (SubscribeSignedVAARequest) returns (stream SubscribeSignedVAAResponse) { + option (google.api.http) = { + post: "/v1:subscribe_signed_vaa" + body: "*" + }; + } +} + +// A MessageFilter represents an exact match for an emitter. +message EmitterFilter { + // Source chain + publicrpc.v1.ChainID chain_id = 1; + // Hex-encoded (without leading 0x) emitter address. + string emitter_address = 2; +} + +message FilterEntry { + oneof filter { + EmitterFilter emitter_filter = 1; + } +} + +message SubscribeSignedVAARequest { + // List of filters to apply to the stream (OR). + // If empty, all messages are streamed. + repeated FilterEntry filters = 1; +} + +message SubscribeSignedVAAResponse { + // Raw VAA bytes + bytes vaa_bytes = 1; +} diff --git a/tools/build.sh b/tools/build.sh index 3b65890b0..fc1e597fa 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -7,3 +7,4 @@ go build -mod=readonly -o bin/protoc-gen-openapiv2 github.com/grpc-ecosystem/grp go build -mod=readonly -o bin/protoc-gen-go-grpc google.golang.org/grpc/cmd/protoc-gen-go-grpc go build -mod=readonly -o bin/buf github.com/bufbuild/buf/cmd/buf go build -mod=readonly -o bin/cobra github.com/spf13/cobra/cobra +go build -mod=readonly -o bin/grpcurl github.com/fullstorydev/grpcurl/cmd/grpcurl diff --git a/tools/go.mod b/tools/go.mod index b3f7bc880..e1c0ec880 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -5,8 +5,107 @@ go 1.17 require ( github.com/bufbuild/buf v0.48.2 github.com/buildpacks/pack v0.20.0 + github.com/fullstorydev/grpcurl v1.8.5 github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 github.com/spf13/cobra v1.2.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 google.golang.org/protobuf v1.27.1 ) + +require ( + cloud.google.com/go v0.81.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab // indirect + github.com/Microsoft/hcsshim v0.8.10 // indirect + github.com/apex/log v1.9.0 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/buildpacks/imgutil v0.0.0-20210510154637-009f91f52918 // indirect + github.com/buildpacks/lifecycle v0.11.3 // indirect + github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 // indirect + github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed // indirect + github.com/containerd/containerd v1.4.1 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.4.1 // indirect + github.com/docker/cli v0.0.0-20200312141509-ef2f64abbd37 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v20.10.7+incompatible // indirect + github.com/docker/docker-credential-helpers v0.6.3 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 // indirect + github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/go-containerregistry v0.5.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/heroku/color v0.0.6 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jhump/protoreflect v1.10.1 // indirect + github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect + github.com/klauspost/compress v1.13.1 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/moby/sys/mount v0.2.0 // indirect + github.com/moby/sys/mountinfo v0.4.0 // indirect + github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v0.1.1 // indirect + github.com/opencontainers/selinux v1.6.0 // indirect + github.com/pelletier/go-toml v1.9.3 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/profile v1.6.0 // indirect + github.com/sabhiram/go-gitignore v0.0.0-20201211074657-223ce5d391b0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/sirupsen/logrus v1.7.0 // indirect + github.com/spf13/afero v1.6.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.8.1 // indirect + github.com/src-d/gcfg v1.4.0 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/twitchtv/twirp v8.1.0+incompatible // indirect + github.com/willf/bitset v1.1.11 // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.7.0 // indirect + go.uber.org/zap v1.18.1 // indirect + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect + golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b // indirect + google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5 // indirect + gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect + gopkg.in/src-d/go-git.v4 v4.13.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) diff --git a/tools/go.sum b/tools/go.sum index 8855811ea..95ee7cef0 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -18,6 +18,7 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -52,6 +53,7 @@ github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tT github.com/Microsoft/hcsshim v0.8.10 h1:k5wTrpnVU2/xv8ZuzGkbXVd3js5zJ8RnumPo5RxiIxU= github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -93,7 +95,9 @@ github.com/buildpacks/lifecycle v0.11.3 h1:FyvtzNxNjnBAdujzUiSpiCap3x+NzrqokGj69 github.com/buildpacks/lifecycle v0.11.3/go.mod h1:4anPUHYqREC3oh3qqKZwt7wqWR866E7BvtIxRE8xGLE= github.com/buildpacks/pack v0.20.0 h1:MkMkfnMcuk1eIBU9qqd3JCAMdB1BqcjMzb/YQf7vB38= github.com/buildpacks/pack v0.20.0/go.mod h1:VmSAGBQ1jO8Ht+SO5GWwsh501ZerxrgJN0fVt1wZM3U= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -102,7 +106,9 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -165,13 +171,17 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullstorydev/grpcurl v1.8.5 h1:xYZBGwhLFuHx6VZLdANGx7Ffb/dlY8JZlJz76/TxclM= +github.com/fullstorydev/grpcurl v1.8.5/go.mod h1:hmAJ/1FHD4xEdiTQS4Byb5NHVVGZr9iGy8CnX0t6ftA= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -285,6 +295,7 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -333,8 +344,9 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jhump/protoreflect v1.10.1 h1:iH+UZfsbRE6vpyZH7asAjTPWJf7RJbpZ9j/N3lDlKs0= +github.com/jhump/protoreflect v1.10.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -509,6 +521,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= @@ -707,6 +720,7 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1 h1:x622Z2o4hgCr/4CiKWc51jHVKaWdtVpBNmEI8wI9Qns= golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -906,6 +920,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -973,6 +988,7 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5 h1:jeEzNnOogdiVxvaPNbt/QFOggkBTaUPBkQ2/NIngeyk= diff --git a/tools/tools.go b/tools/tools.go index 2ff4746a2..860165d00 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -9,6 +9,7 @@ package main import ( _ "github.com/bufbuild/buf/cmd/buf" _ "github.com/buildpacks/pack/cmd/pack" + _ "github.com/fullstorydev/grpcurl/cmd/grpcurl" _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" _ "github.com/spf13/cobra/cobra"