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 := ðpb.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 }