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  }