node: add spy service

Change-Id: Ieb04e6d26c7778d8a8afbbeaee79d764d9f2cd31
This commit is contained in:
Leo 2021-12-03 01:02:32 +01:00
parent 2e0a225c23
commit bc48b1b51d
20 changed files with 662 additions and 81 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

60
devnet/spy.yaml Normal file
View File

@ -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

View File

@ -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
}

View File

@ -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])

View File

@ -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))
}

View File

@ -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

View File

@ -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)

322
node/cmd/spy/spy.go Normal file
View File

@ -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
}

30
node/pkg/common/grpc.go Normal file
View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()

View File

@ -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;

44
proto/spy/v1/spy.proto Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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
)

View File

@ -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=

View File

@ -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"