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(&ethpb.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(&ethpb.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(&ethpb_v1alpha1.SignedBeaconBlock{
    52  			Block: &ethpb_v1alpha1.BeaconBlock{
    53  				Slot: 8,
    54  			},
    55  		})
    56  		wantedBlockRoot, err := wantedBlock.HashTreeRoot()
    57  		require.NoError(t, err)
    58  		genericResponse, err := anypb.New(&ethpb.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(&ethpb_v1alpha1.Attestation{
    92  			Data: &ethpb_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 := &ethpb_v1alpha1.AggregateAttestationAndProof{
   126  			Aggregate: testutil.HydrateAttestation(&ethpb_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 := &ethpb_v1alpha1.SignedVoluntaryExit{
   158  			Exit: &ethpb_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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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  }