github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/events/events.go (about)

     1  package events
     2  
     3  import (
     4  	gwpb "github.com/grpc-ecosystem/grpc-gateway/v2/proto/gateway"
     5  	"github.com/pkg/errors"
     6  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
     7  	blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
     9  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    10  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1"
    11  	"github.com/prysmaticlabs/prysm/proto/migration"
    12  	"google.golang.org/grpc/codes"
    13  	"google.golang.org/grpc/status"
    14  	"google.golang.org/protobuf/proto"
    15  	"google.golang.org/protobuf/types/known/anypb"
    16  )
    17  
    18  const (
    19  	// HeadTopic represents a new chain head event topic.
    20  	HeadTopic = "head"
    21  	// BlockTopic represents a new produced block event topic.
    22  	BlockTopic = "block"
    23  	// AttestationTopic represents a new submitted attestation event topic.
    24  	AttestationTopic = "attestation"
    25  	// VoluntaryExitTopic represents a new performed voluntary exit event topic.
    26  	VoluntaryExitTopic = "voluntary_exit"
    27  	// FinalizedCheckpointTopic represents a new finalized checkpoint event topic.
    28  	FinalizedCheckpointTopic = "finalized_checkpoint"
    29  	// ChainReorgTopic represents a chain reorganization event topic.
    30  	ChainReorgTopic = "chain_reorg"
    31  )
    32  
    33  var casesHandled = map[string]bool{
    34  	HeadTopic:                true,
    35  	BlockTopic:               true,
    36  	AttestationTopic:         true,
    37  	VoluntaryExitTopic:       true,
    38  	FinalizedCheckpointTopic: true,
    39  	ChainReorgTopic:          true,
    40  }
    41  
    42  // StreamEvents allows requesting all events from a set of topics defined in the Ethereum consensus API standard.
    43  // The topics supported include block events, attestations, chain reorgs, voluntary exits,
    44  // chain finality, and more.
    45  func (s *Server) StreamEvents(
    46  	req *ethpb.StreamEventsRequest, stream ethpb.Events_StreamEventsServer,
    47  ) error {
    48  	if req == nil || len(req.Topics) == 0 {
    49  		return status.Error(codes.InvalidArgument, "No topics specified to subscribe to")
    50  	}
    51  	// Check if the topics in the request are valid.
    52  	requestedTopics := make(map[string]bool)
    53  	for _, topic := range req.Topics {
    54  		if _, ok := casesHandled[topic]; !ok {
    55  			return status.Errorf(codes.InvalidArgument, "Topic %s not allowed for event subscriptions", topic)
    56  		}
    57  		requestedTopics[topic] = true
    58  	}
    59  
    60  	// Subscribe to event feeds from information received in the beacon node runtime.
    61  	blockChan := make(chan *feed.Event, 1)
    62  	blockSub := s.BlockNotifier.BlockFeed().Subscribe(blockChan)
    63  
    64  	opsChan := make(chan *feed.Event, 1)
    65  	opsSub := s.OperationNotifier.OperationFeed().Subscribe(opsChan)
    66  
    67  	stateChan := make(chan *feed.Event, 1)
    68  	stateSub := s.StateNotifier.StateFeed().Subscribe(stateChan)
    69  
    70  	defer blockSub.Unsubscribe()
    71  	defer opsSub.Unsubscribe()
    72  	defer stateSub.Unsubscribe()
    73  
    74  	// Handle each event received and context cancelation.
    75  	for {
    76  		select {
    77  		case event := <-blockChan:
    78  			if err := s.handleBlockEvents(stream, requestedTopics, event); err != nil {
    79  				return status.Errorf(codes.Internal, "Could not handle block event: %v", err)
    80  			}
    81  		case event := <-opsChan:
    82  			if err := s.handleBlockOperationEvents(stream, requestedTopics, event); err != nil {
    83  				return status.Errorf(codes.Internal, "Could not handle block operations event: %v", err)
    84  			}
    85  		case event := <-stateChan:
    86  			if err := s.handleStateEvents(stream, requestedTopics, event); err != nil {
    87  				return status.Errorf(codes.Internal, "Could not handle state event: %v", err)
    88  			}
    89  		case <-s.Ctx.Done():
    90  			return status.Errorf(codes.Canceled, "Context canceled")
    91  		case <-stream.Context().Done():
    92  			return status.Errorf(codes.Canceled, "Context canceled")
    93  		}
    94  	}
    95  }
    96  
    97  func (s *Server) handleBlockEvents(
    98  	stream ethpb.Events_StreamEventsServer, requestedTopics map[string]bool, event *feed.Event,
    99  ) error {
   100  	switch event.Type {
   101  	case blockfeed.ReceivedBlock:
   102  		if _, ok := requestedTopics[BlockTopic]; !ok {
   103  			return nil
   104  		}
   105  		blkData, ok := event.Data.(*blockfeed.ReceivedBlockData)
   106  		if !ok {
   107  			return nil
   108  		}
   109  		v1Data, err := migration.BlockIfaceToV1BlockHeader(blkData.SignedBlock)
   110  		if err != nil {
   111  			return err
   112  		}
   113  		item, err := v1Data.HashTreeRoot()
   114  		if err != nil {
   115  			return errors.Wrap(err, "could not hash tree root block")
   116  		}
   117  		eventBlock := &ethpb.EventBlock{
   118  			Slot:  v1Data.Message.Slot,
   119  			Block: item[:],
   120  		}
   121  		return s.streamData(stream, BlockTopic, eventBlock)
   122  	default:
   123  		return nil
   124  	}
   125  }
   126  
   127  func (s *Server) handleBlockOperationEvents(
   128  	stream ethpb.Events_StreamEventsServer, requestedTopics map[string]bool, event *feed.Event,
   129  ) error {
   130  	switch event.Type {
   131  	case operation.AggregatedAttReceived:
   132  		if _, ok := requestedTopics[AttestationTopic]; !ok {
   133  			return nil
   134  		}
   135  		attData, ok := event.Data.(*operation.AggregatedAttReceivedData)
   136  		if !ok {
   137  			return nil
   138  		}
   139  		v1Data := migration.V1Alpha1AggregateAttAndProofToV1(attData.Attestation)
   140  		return s.streamData(stream, AttestationTopic, v1Data)
   141  	case operation.UnaggregatedAttReceived:
   142  		if _, ok := requestedTopics[AttestationTopic]; !ok {
   143  			return nil
   144  		}
   145  		attData, ok := event.Data.(*operation.UnAggregatedAttReceivedData)
   146  		if !ok {
   147  			return nil
   148  		}
   149  		v1Data := migration.V1Alpha1AttestationToV1(attData.Attestation)
   150  		return s.streamData(stream, AttestationTopic, v1Data)
   151  	case operation.ExitReceived:
   152  		if _, ok := requestedTopics[VoluntaryExitTopic]; !ok {
   153  			return nil
   154  		}
   155  		exitData, ok := event.Data.(*operation.ExitReceivedData)
   156  		if !ok {
   157  			return nil
   158  		}
   159  		v1Data := migration.V1Alpha1ExitToV1(exitData.Exit)
   160  		return s.streamData(stream, VoluntaryExitTopic, v1Data)
   161  	default:
   162  		return nil
   163  	}
   164  }
   165  
   166  func (s *Server) handleStateEvents(
   167  	stream ethpb.Events_StreamEventsServer, requestedTopics map[string]bool, event *feed.Event,
   168  ) error {
   169  	switch event.Type {
   170  	case statefeed.NewHead:
   171  		if _, ok := requestedTopics[HeadTopic]; !ok {
   172  			return nil
   173  		}
   174  		head, ok := event.Data.(*ethpb.EventHead)
   175  		if !ok {
   176  			return nil
   177  		}
   178  		return s.streamData(stream, HeadTopic, head)
   179  	case statefeed.FinalizedCheckpoint:
   180  		if _, ok := requestedTopics[FinalizedCheckpointTopic]; !ok {
   181  			return nil
   182  		}
   183  		finalizedCheckpoint, ok := event.Data.(*ethpb.EventFinalizedCheckpoint)
   184  		if !ok {
   185  			return nil
   186  		}
   187  		return s.streamData(stream, FinalizedCheckpointTopic, finalizedCheckpoint)
   188  	case statefeed.Reorg:
   189  		if _, ok := requestedTopics[ChainReorgTopic]; !ok {
   190  			return nil
   191  		}
   192  		reorg, ok := event.Data.(*ethpb.EventChainReorg)
   193  		if !ok {
   194  			return nil
   195  		}
   196  		return s.streamData(stream, ChainReorgTopic, reorg)
   197  	default:
   198  		return nil
   199  	}
   200  }
   201  
   202  func (s *Server) streamData(stream ethpb.Events_StreamEventsServer, name string, data proto.Message) error {
   203  	returnData, err := anypb.New(data)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	return stream.Send(&gwpb.EventSource{
   208  		Event: name,
   209  		Data:  returnData,
   210  	})
   211  }