github.com/prysmaticlabs/prysm@v1.4.4/slasher/beaconclient/service.go (about) 1 /* 2 Package beaconclient defines a service that interacts with a beacon 3 node via a gRPC client to listen for streamed blocks, attestations, and to 4 submit proposer/attester slashings to the node in case they are detected. 5 */ 6 package beaconclient 7 8 import ( 9 "context" 10 11 middleware "github.com/grpc-ecosystem/go-grpc-middleware" 12 grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" 13 grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 14 grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 15 "github.com/pkg/errors" 16 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 17 "github.com/prysmaticlabs/prysm/shared/event" 18 "github.com/prysmaticlabs/prysm/shared/grpcutils" 19 "github.com/prysmaticlabs/prysm/slasher/cache" 20 "github.com/prysmaticlabs/prysm/slasher/db" 21 "go.opencensus.io/plugin/ocgrpc" 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/credentials" 24 ) 25 26 // Notifier defines a struct which exposes event feeds regarding beacon blocks, 27 // attestations, and more information received from a beacon node. 28 type Notifier interface { 29 BlockFeed() *event.Feed 30 AttestationFeed() *event.Feed 31 ClientReadyFeed() *event.Feed 32 } 33 34 // ChainFetcher defines a struct which can retrieve 35 // chain information from a beacon node such as the latest chain head. 36 type ChainFetcher interface { 37 ChainHead(ctx context.Context) (*ethpb.ChainHead, error) 38 } 39 40 // Service struct for the beaconclient service of the slasher. 41 type Service struct { 42 cfg *Config 43 ctx context.Context 44 cancel context.CancelFunc 45 conn *grpc.ClientConn 46 clientFeed *event.Feed 47 blockFeed *event.Feed 48 attestationFeed *event.Feed 49 proposerSlashingsChan chan *ethpb.ProposerSlashing 50 attesterSlashingsChan chan *ethpb.AttesterSlashing 51 receivedAttestationsBuffer chan *ethpb.IndexedAttestation 52 collectedAttestationsBuffer chan []*ethpb.IndexedAttestation 53 publicKeyCache *cache.PublicKeyCache 54 genesisValidatorRoot []byte 55 beaconDialOptions []grpc.DialOption 56 } 57 58 // Config options for the beaconclient service. 59 type Config struct { 60 BeaconProvider string 61 BeaconCert string 62 SlasherDB db.Database 63 ProposerSlashingsFeed *event.Feed 64 AttesterSlashingsFeed *event.Feed 65 BeaconClient ethpb.BeaconChainClient 66 NodeClient ethpb.NodeClient 67 } 68 69 // NewService instantiation. 70 func NewService(ctx context.Context, cfg *Config) (*Service, error) { 71 ctx, cancel := context.WithCancel(ctx) 72 _ = cancel // govet fix for lost cancel. Cancel is handled in service.Stop() 73 publicKeyCache, err := cache.NewPublicKeyCache(0, nil) 74 if err != nil { 75 return nil, errors.Wrap(err, "could not create new cache") 76 } 77 78 return &Service{ 79 cfg: cfg, 80 ctx: ctx, 81 cancel: cancel, 82 blockFeed: new(event.Feed), 83 clientFeed: new(event.Feed), 84 attestationFeed: new(event.Feed), 85 proposerSlashingsChan: make(chan *ethpb.ProposerSlashing, 1), 86 attesterSlashingsChan: make(chan *ethpb.AttesterSlashing, 1), 87 receivedAttestationsBuffer: make(chan *ethpb.IndexedAttestation, 1), 88 collectedAttestationsBuffer: make(chan []*ethpb.IndexedAttestation, 1), 89 publicKeyCache: publicKeyCache, 90 }, nil 91 } 92 93 // BlockFeed returns a feed other services in slasher can subscribe to 94 // blocks received via the beacon node through gRPC. 95 func (s *Service) BlockFeed() *event.Feed { 96 return s.blockFeed 97 } 98 99 // AttestationFeed returns a feed other services in slasher can subscribe to 100 // attestations received via the beacon node through gRPC. 101 func (s *Service) AttestationFeed() *event.Feed { 102 return s.attestationFeed 103 } 104 105 // ClientReadyFeed returns a feed other services in slasher can subscribe to 106 // to indicate when the gRPC connection is ready. 107 func (s *Service) ClientReadyFeed() *event.Feed { 108 return s.clientFeed 109 } 110 111 // Stop the beacon client service by closing the gRPC connection. 112 func (s *Service) Stop() error { 113 s.cancel() 114 log.Info("Stopping service") 115 if s.conn != nil { 116 return s.conn.Close() 117 } 118 return nil 119 } 120 121 // Status returns an error if there exists a gRPC connection error 122 // in the service. 123 func (s *Service) Status() error { 124 if s.conn == nil { 125 return errors.New("no connection to beacon RPC") 126 } 127 return nil 128 } 129 130 // Start the main runtime of the beaconclient service, initializing 131 // a gRPC client connection with a beacon node, listening for 132 // streamed blocks/attestations, and submitting slashing operations 133 // after they are detected by other services in the slasher. 134 func (s *Service) Start() { 135 var dialOpt grpc.DialOption 136 if s.cfg.BeaconCert != "" { 137 creds, err := credentials.NewClientTLSFromFile(s.cfg.BeaconCert, "") 138 if err != nil { 139 log.Errorf("Could not get valid credentials: %v", err) 140 } 141 dialOpt = grpc.WithTransportCredentials(creds) 142 } else { 143 dialOpt = grpc.WithInsecure() 144 log.Warn( 145 "You are using an insecure gRPC connection to beacon chain! Please provide a certificate and key to use a secure connection", 146 ) 147 } 148 beaconOpts := []grpc.DialOption{ 149 dialOpt, 150 grpc.WithStatsHandler(&ocgrpc.ClientHandler{}), 151 grpc.WithStreamInterceptor(middleware.ChainStreamClient( 152 grpc_opentracing.StreamClientInterceptor(), 153 grpc_prometheus.StreamClientInterceptor, 154 grpc_retry.StreamClientInterceptor(), 155 grpcutils.LogStream, 156 )), 157 grpc.WithUnaryInterceptor(middleware.ChainUnaryClient( 158 grpc_opentracing.UnaryClientInterceptor(), 159 grpc_prometheus.UnaryClientInterceptor, 160 grpc_retry.UnaryClientInterceptor(), 161 grpcutils.LogRequests, 162 )), 163 } 164 conn, err := grpc.DialContext(s.ctx, s.cfg.BeaconProvider, beaconOpts...) 165 if err != nil { 166 log.Fatalf("Could not dial endpoint: %s, %v", s.cfg.BeaconProvider, err) 167 } 168 s.beaconDialOptions = beaconOpts 169 log.Info("Successfully started gRPC connection") 170 s.conn = conn 171 s.cfg.BeaconClient = ethpb.NewBeaconChainClient(s.conn) 172 s.cfg.NodeClient = ethpb.NewNodeClient(s.conn) 173 174 // We poll for the sync status of the beacon node until it is fully synced. 175 s.querySyncStatus(s.ctx) 176 177 // We notify other services in slasher that the beacon client is ready 178 // and the connection is active. 179 s.clientFeed.Send(true) 180 181 // We register subscribers for any detected proposer/attester slashings 182 // in the slasher services that we can submit to the beacon node 183 // as they are found. 184 go s.subscribeDetectedProposerSlashings(s.ctx, s.proposerSlashingsChan) 185 go s.subscribeDetectedAttesterSlashings(s.ctx, s.attesterSlashingsChan) 186 187 }