github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/events/events_test.go (about) 1 package events 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/golang/mock/gomock" 8 "github.com/grpc-ecosystem/grpc-gateway/v2/proto/gateway" 9 mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 11 blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block" 12 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" 13 statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" 14 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" 15 ethpb_v1alpha1 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 16 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 17 "github.com/prysmaticlabs/prysm/proto/migration" 18 "github.com/prysmaticlabs/prysm/shared/event" 19 "github.com/prysmaticlabs/prysm/shared/mock" 20 "github.com/prysmaticlabs/prysm/shared/testutil" 21 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 22 "github.com/prysmaticlabs/prysm/shared/testutil/require" 23 "google.golang.org/protobuf/types/known/anypb" 24 ) 25 26 func TestStreamEvents_Preconditions(t *testing.T) { 27 t.Run("no_topics_specified", func(t *testing.T) { 28 srv := &Server{} 29 ctrl := gomock.NewController(t) 30 defer ctrl.Finish() 31 mockStream := mock.NewMockEvents_StreamEventsServer(ctrl) 32 err := srv.StreamEvents(ðpb.StreamEventsRequest{Topics: nil}, mockStream) 33 require.ErrorContains(t, "No topics specified", err) 34 }) 35 t.Run("topic_not_allowed", func(t *testing.T) { 36 srv := &Server{} 37 ctrl := gomock.NewController(t) 38 defer ctrl.Finish() 39 mockStream := mock.NewMockEvents_StreamEventsServer(ctrl) 40 err := srv.StreamEvents(ðpb.StreamEventsRequest{Topics: []string{"foobar"}}, mockStream) 41 require.ErrorContains(t, "Topic foobar not allowed", err) 42 }) 43 } 44 45 func TestStreamEvents_BlockEvents(t *testing.T) { 46 t.Run(BlockTopic, func(t *testing.T) { 47 ctx := context.Background() 48 srv, ctrl, mockStream := setupServer(ctx, t) 49 defer ctrl.Finish() 50 51 wantedBlock := testutil.HydrateSignedBeaconBlock(ðpb_v1alpha1.SignedBeaconBlock{ 52 Block: ðpb_v1alpha1.BeaconBlock{ 53 Slot: 8, 54 }, 55 }) 56 wantedBlockRoot, err := wantedBlock.HashTreeRoot() 57 require.NoError(t, err) 58 genericResponse, err := anypb.New(ðpb.EventBlock{ 59 Slot: 8, 60 Block: wantedBlockRoot[:], 61 }) 62 require.NoError(t, err) 63 wantedMessage := &gateway.EventSource{ 64 Event: BlockTopic, 65 Data: genericResponse, 66 } 67 68 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 69 t: t, 70 srv: srv, 71 topics: []string{BlockTopic}, 72 stream: mockStream, 73 shouldReceive: wantedMessage, 74 itemToSend: &feed.Event{ 75 Type: blockfeed.ReceivedBlock, 76 Data: &blockfeed.ReceivedBlockData{ 77 SignedBlock: wrapper.WrappedPhase0SignedBeaconBlock(wantedBlock), 78 }, 79 }, 80 feed: srv.BlockNotifier.BlockFeed(), 81 }) 82 }) 83 } 84 85 func TestStreamEvents_OperationsEvents(t *testing.T) { 86 t.Run("attestation_unaggregated", func(t *testing.T) { 87 ctx := context.Background() 88 srv, ctrl, mockStream := setupServer(ctx, t) 89 defer ctrl.Finish() 90 91 wantedAttV1alpha1 := testutil.HydrateAttestation(ðpb_v1alpha1.Attestation{ 92 Data: ðpb_v1alpha1.AttestationData{ 93 Slot: 8, 94 }, 95 }) 96 wantedAtt := migration.V1Alpha1AttestationToV1(wantedAttV1alpha1) 97 genericResponse, err := anypb.New(wantedAtt) 98 require.NoError(t, err) 99 100 wantedMessage := &gateway.EventSource{ 101 Event: AttestationTopic, 102 Data: genericResponse, 103 } 104 105 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 106 t: t, 107 srv: srv, 108 topics: []string{AttestationTopic}, 109 stream: mockStream, 110 shouldReceive: wantedMessage, 111 itemToSend: &feed.Event{ 112 Type: operation.UnaggregatedAttReceived, 113 Data: &operation.UnAggregatedAttReceivedData{ 114 Attestation: wantedAttV1alpha1, 115 }, 116 }, 117 feed: srv.OperationNotifier.OperationFeed(), 118 }) 119 }) 120 t.Run("attestation_aggregated", func(t *testing.T) { 121 ctx := context.Background() 122 srv, ctrl, mockStream := setupServer(ctx, t) 123 defer ctrl.Finish() 124 125 wantedAttV1alpha1 := ðpb_v1alpha1.AggregateAttestationAndProof{ 126 Aggregate: testutil.HydrateAttestation(ðpb_v1alpha1.Attestation{}), 127 } 128 wantedAtt := migration.V1Alpha1AggregateAttAndProofToV1(wantedAttV1alpha1) 129 genericResponse, err := anypb.New(wantedAtt) 130 require.NoError(t, err) 131 132 wantedMessage := &gateway.EventSource{ 133 Event: AttestationTopic, 134 Data: genericResponse, 135 } 136 137 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 138 t: t, 139 srv: srv, 140 topics: []string{AttestationTopic}, 141 stream: mockStream, 142 shouldReceive: wantedMessage, 143 itemToSend: &feed.Event{ 144 Type: operation.AggregatedAttReceived, 145 Data: &operation.AggregatedAttReceivedData{ 146 Attestation: wantedAttV1alpha1, 147 }, 148 }, 149 feed: srv.OperationNotifier.OperationFeed(), 150 }) 151 }) 152 t.Run(VoluntaryExitTopic, func(t *testing.T) { 153 ctx := context.Background() 154 srv, ctrl, mockStream := setupServer(ctx, t) 155 defer ctrl.Finish() 156 157 wantedExitV1alpha1 := ðpb_v1alpha1.SignedVoluntaryExit{ 158 Exit: ðpb_v1alpha1.VoluntaryExit{ 159 Epoch: 1, 160 ValidatorIndex: 1, 161 }, 162 Signature: make([]byte, 96), 163 } 164 wantedExit := migration.V1Alpha1ExitToV1(wantedExitV1alpha1) 165 genericResponse, err := anypb.New(wantedExit) 166 require.NoError(t, err) 167 168 wantedMessage := &gateway.EventSource{ 169 Event: VoluntaryExitTopic, 170 Data: genericResponse, 171 } 172 173 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 174 t: t, 175 srv: srv, 176 topics: []string{VoluntaryExitTopic}, 177 stream: mockStream, 178 shouldReceive: wantedMessage, 179 itemToSend: &feed.Event{ 180 Type: operation.ExitReceived, 181 Data: &operation.ExitReceivedData{ 182 Exit: wantedExitV1alpha1, 183 }, 184 }, 185 feed: srv.OperationNotifier.OperationFeed(), 186 }) 187 }) 188 } 189 190 func TestStreamEvents_StateEvents(t *testing.T) { 191 t.Run(HeadTopic, func(t *testing.T) { 192 ctx := context.Background() 193 srv, ctrl, mockStream := setupServer(ctx, t) 194 defer ctrl.Finish() 195 196 wantedHead := ðpb.EventHead{ 197 Slot: 8, 198 Block: make([]byte, 32), 199 State: make([]byte, 32), 200 EpochTransition: true, 201 PreviousDutyDependentRoot: make([]byte, 32), 202 CurrentDutyDependentRoot: make([]byte, 32), 203 } 204 genericResponse, err := anypb.New(wantedHead) 205 require.NoError(t, err) 206 wantedMessage := &gateway.EventSource{ 207 Event: HeadTopic, 208 Data: genericResponse, 209 } 210 211 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 212 t: t, 213 srv: srv, 214 topics: []string{HeadTopic}, 215 stream: mockStream, 216 shouldReceive: wantedMessage, 217 itemToSend: &feed.Event{ 218 Type: statefeed.NewHead, 219 Data: wantedHead, 220 }, 221 feed: srv.StateNotifier.StateFeed(), 222 }) 223 }) 224 t.Run(FinalizedCheckpointTopic, func(t *testing.T) { 225 ctx := context.Background() 226 srv, ctrl, mockStream := setupServer(ctx, t) 227 defer ctrl.Finish() 228 229 wantedCheckpoint := ðpb.EventFinalizedCheckpoint{ 230 Block: make([]byte, 32), 231 State: make([]byte, 32), 232 Epoch: 8, 233 } 234 genericResponse, err := anypb.New(wantedCheckpoint) 235 require.NoError(t, err) 236 wantedMessage := &gateway.EventSource{ 237 Event: FinalizedCheckpointTopic, 238 Data: genericResponse, 239 } 240 241 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 242 t: t, 243 srv: srv, 244 topics: []string{FinalizedCheckpointTopic}, 245 stream: mockStream, 246 shouldReceive: wantedMessage, 247 itemToSend: &feed.Event{ 248 Type: statefeed.FinalizedCheckpoint, 249 Data: wantedCheckpoint, 250 }, 251 feed: srv.StateNotifier.StateFeed(), 252 }) 253 }) 254 t.Run(ChainReorgTopic, func(t *testing.T) { 255 ctx := context.Background() 256 srv, ctrl, mockStream := setupServer(ctx, t) 257 defer ctrl.Finish() 258 259 wantedReorg := ðpb.EventChainReorg{ 260 Slot: 8, 261 Depth: 1, 262 OldHeadBlock: make([]byte, 32), 263 NewHeadBlock: make([]byte, 32), 264 OldHeadState: make([]byte, 32), 265 NewHeadState: make([]byte, 32), 266 Epoch: 0, 267 } 268 genericResponse, err := anypb.New(wantedReorg) 269 require.NoError(t, err) 270 wantedMessage := &gateway.EventSource{ 271 Event: ChainReorgTopic, 272 Data: genericResponse, 273 } 274 275 assertFeedSendAndReceive(ctx, &assertFeedArgs{ 276 t: t, 277 srv: srv, 278 topics: []string{ChainReorgTopic}, 279 stream: mockStream, 280 shouldReceive: wantedMessage, 281 itemToSend: &feed.Event{ 282 Type: statefeed.Reorg, 283 Data: wantedReorg, 284 }, 285 feed: srv.StateNotifier.StateFeed(), 286 }) 287 }) 288 } 289 290 func setupServer(ctx context.Context, t testing.TB) (*Server, *gomock.Controller, *mock.MockEvents_StreamEventsServer) { 291 srv := &Server{ 292 BlockNotifier: &mockChain.MockBlockNotifier{}, 293 StateNotifier: &mockChain.MockStateNotifier{}, 294 OperationNotifier: &mockChain.MockOperationNotifier{}, 295 Ctx: ctx, 296 } 297 ctrl := gomock.NewController(t) 298 mockStream := mock.NewMockEvents_StreamEventsServer(ctrl) 299 return srv, ctrl, mockStream 300 } 301 302 type assertFeedArgs struct { 303 t *testing.T 304 topics []string 305 srv *Server 306 stream *mock.MockEvents_StreamEventsServer 307 shouldReceive interface{} 308 itemToSend *feed.Event 309 feed *event.Feed 310 } 311 312 func assertFeedSendAndReceive(ctx context.Context, args *assertFeedArgs) { 313 exitRoutine := make(chan bool) 314 defer close(exitRoutine) 315 args.stream.EXPECT().Send(args.shouldReceive).Do(func(arg0 interface{}) { 316 exitRoutine <- true 317 }) 318 args.stream.EXPECT().Context().Return(ctx).AnyTimes() 319 320 req := ðpb.StreamEventsRequest{Topics: args.topics} 321 go func(tt *testing.T) { 322 assert.NoError(tt, args.srv.StreamEvents(req, args.stream), "Could not call RPC method") 323 }(args.t) 324 // Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed). 325 for sent := 0; sent == 0; { 326 sent = args.feed.Send(args.itemToSend) 327 } 328 <-exitRoutine 329 }