github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/attester_test.go (about)

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"math/rand"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/cache"
    13  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    14  	dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
    16  	mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    17  	"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
    18  	v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    19  	mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
    20  	pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    21  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    22  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    23  	"github.com/prysmaticlabs/prysm/shared/bls"
    24  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    25  	"github.com/prysmaticlabs/prysm/shared/params"
    26  	"github.com/prysmaticlabs/prysm/shared/testutil"
    27  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    28  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    29  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    30  	"google.golang.org/protobuf/proto"
    31  )
    32  
    33  func TestProposeAttestation_OK(t *testing.T) {
    34  	db := dbutil.SetupDB(t)
    35  	ctx := context.Background()
    36  
    37  	attesterServer := &Server{
    38  		HeadFetcher:       &mock.ChainService{},
    39  		P2P:               &mockp2p.MockBroadcaster{},
    40  		BeaconDB:          db,
    41  		AttestationCache:  cache.NewAttestationCache(),
    42  		AttPool:           attestations.NewPool(),
    43  		OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
    44  	}
    45  	head := testutil.NewBeaconBlock()
    46  	head.Block.Slot = 999
    47  	head.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32)
    48  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(head)))
    49  	root, err := head.Block.HashTreeRoot()
    50  	require.NoError(t, err)
    51  
    52  	validators := make([]*ethpb.Validator, 64)
    53  	for i := 0; i < len(validators); i++ {
    54  		validators[i] = &ethpb.Validator{
    55  			PublicKey:             make([]byte, 48),
    56  			WithdrawalCredentials: make([]byte, 32),
    57  			ExitEpoch:             params.BeaconConfig().FarFutureEpoch,
    58  			EffectiveBalance:      params.BeaconConfig().MaxEffectiveBalance,
    59  		}
    60  	}
    61  
    62  	state, err := testutil.NewBeaconState()
    63  	require.NoError(t, err)
    64  	require.NoError(t, state.SetSlot(params.BeaconConfig().SlotsPerEpoch+1))
    65  	require.NoError(t, state.SetValidators(validators))
    66  	require.NoError(t, db.SaveState(ctx, state, root))
    67  	require.NoError(t, db.SaveHeadBlockRoot(ctx, root))
    68  
    69  	sk, err := bls.RandKey()
    70  	require.NoError(t, err)
    71  	sig := sk.Sign([]byte("dummy_test_data"))
    72  	req := &ethpb.Attestation{
    73  		Signature: sig.Marshal(),
    74  		Data: &ethpb.AttestationData{
    75  			BeaconBlockRoot: root[:],
    76  			Source:          &ethpb.Checkpoint{Root: make([]byte, 32)},
    77  			Target:          &ethpb.Checkpoint{Root: make([]byte, 32)},
    78  		},
    79  	}
    80  	_, err = attesterServer.ProposeAttestation(context.Background(), req)
    81  	assert.NoError(t, err)
    82  }
    83  
    84  func TestProposeAttestation_IncorrectSignature(t *testing.T) {
    85  	db := dbutil.SetupDB(t)
    86  
    87  	attesterServer := &Server{
    88  		HeadFetcher:       &mock.ChainService{},
    89  		P2P:               &mockp2p.MockBroadcaster{},
    90  		BeaconDB:          db,
    91  		AttestationCache:  cache.NewAttestationCache(),
    92  		AttPool:           attestations.NewPool(),
    93  		OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
    94  	}
    95  
    96  	req := testutil.HydrateAttestation(&ethpb.Attestation{})
    97  	wanted := "Incorrect attestation signature"
    98  	_, err := attesterServer.ProposeAttestation(context.Background(), req)
    99  	assert.ErrorContains(t, wanted, err)
   100  }
   101  
   102  func TestGetAttestationData_OK(t *testing.T) {
   103  	ctx := context.Background()
   104  	db := dbutil.SetupDB(t)
   105  
   106  	block := testutil.NewBeaconBlock()
   107  	block.Block.Slot = 3*params.BeaconConfig().SlotsPerEpoch + 1
   108  	targetBlock := testutil.NewBeaconBlock()
   109  	targetBlock.Block.Slot = 1 * params.BeaconConfig().SlotsPerEpoch
   110  	justifiedBlock := testutil.NewBeaconBlock()
   111  	justifiedBlock.Block.Slot = 2 * params.BeaconConfig().SlotsPerEpoch
   112  	blockRoot, err := block.Block.HashTreeRoot()
   113  	require.NoError(t, err, "Could not hash beacon block")
   114  	justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
   115  	require.NoError(t, err, "Could not get signing root for justified block")
   116  	targetRoot, err := targetBlock.Block.HashTreeRoot()
   117  	require.NoError(t, err, "Could not get signing root for target block")
   118  	slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
   119  	beaconState, err := testutil.NewBeaconState()
   120  	require.NoError(t, err)
   121  	require.NoError(t, beaconState.SetSlot(slot))
   122  	err = beaconState.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{
   123  		Epoch: 2,
   124  		Root:  justifiedRoot[:],
   125  	})
   126  	require.NoError(t, err)
   127  
   128  	blockRoots := beaconState.BlockRoots()
   129  	blockRoots[1] = blockRoot[:]
   130  	blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:]
   131  	blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:]
   132  	require.NoError(t, beaconState.SetBlockRoots(blockRoots))
   133  	chainService := &mock.ChainService{
   134  		Genesis: time.Now(),
   135  	}
   136  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   137  	attesterServer := &Server{
   138  		BeaconDB:         db,
   139  		P2P:              &mockp2p.MockBroadcaster{},
   140  		SyncChecker:      &mockSync.Sync{IsSyncing: false},
   141  		AttestationCache: cache.NewAttestationCache(),
   142  		HeadFetcher: &mock.ChainService{
   143  			State: beaconState, Root: blockRoot[:],
   144  		},
   145  		FinalizationFetcher: &mock.ChainService{
   146  			CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint(),
   147  		},
   148  		TimeFetcher: &mock.ChainService{
   149  			Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second),
   150  		},
   151  		StateNotifier: chainService.StateNotifier(),
   152  	}
   153  	require.NoError(t, db.SaveState(ctx, beaconState, blockRoot))
   154  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
   155  	require.NoError(t, db.SaveHeadBlockRoot(ctx, blockRoot))
   156  
   157  	req := &ethpb.AttestationDataRequest{
   158  		CommitteeIndex: 0,
   159  		Slot:           3*params.BeaconConfig().SlotsPerEpoch + 1,
   160  	}
   161  	res, err := attesterServer.GetAttestationData(context.Background(), req)
   162  	require.NoError(t, err, "Could not get attestation info at slot")
   163  
   164  	expectedInfo := &ethpb.AttestationData{
   165  		Slot:            3*params.BeaconConfig().SlotsPerEpoch + 1,
   166  		BeaconBlockRoot: blockRoot[:],
   167  		Source: &ethpb.Checkpoint{
   168  			Epoch: 2,
   169  			Root:  justifiedRoot[:],
   170  		},
   171  		Target: &ethpb.Checkpoint{
   172  			Epoch: 3,
   173  			Root:  blockRoot[:],
   174  		},
   175  	}
   176  
   177  	if !proto.Equal(res, expectedInfo) {
   178  		t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
   179  	}
   180  }
   181  
   182  func TestGetAttestationData_SyncNotReady(t *testing.T) {
   183  	as := &Server{
   184  		SyncChecker: &mockSync.Sync{IsSyncing: true},
   185  	}
   186  	_, err := as.GetAttestationData(context.Background(), &ethpb.AttestationDataRequest{})
   187  	assert.ErrorContains(t, "Syncing to latest head", err)
   188  }
   189  
   190  func TestAttestationDataAtSlot_HandlesFarAwayJustifiedEpoch(t *testing.T) {
   191  	// Scenario:
   192  	//
   193  	// State slot = 10000
   194  	// Last justified slot = epoch start of 1500
   195  	// HistoricalRootsLimit = 8192
   196  	//
   197  	// More background: https://github.com/prysmaticlabs/prysm/issues/2153
   198  	// This test breaks if it doesnt use mainnet config
   199  	db := dbutil.SetupDB(t)
   200  	ctx := context.Background()
   201  	// Ensure HistoricalRootsLimit matches scenario
   202  	params.SetupTestConfigCleanup(t)
   203  	cfg := params.MainnetConfig()
   204  	cfg.HistoricalRootsLimit = 8192
   205  	params.OverrideBeaconConfig(cfg)
   206  
   207  	block := testutil.NewBeaconBlock()
   208  	block.Block.Slot = 10000
   209  	epochBoundaryBlock := testutil.NewBeaconBlock()
   210  	var err error
   211  	epochBoundaryBlock.Block.Slot, err = helpers.StartSlot(helpers.SlotToEpoch(10000))
   212  	require.NoError(t, err)
   213  	justifiedBlock := testutil.NewBeaconBlock()
   214  	justifiedBlock.Block.Slot, err = helpers.StartSlot(helpers.SlotToEpoch(1500))
   215  	require.NoError(t, err)
   216  	justifiedBlock.Block.Slot -= 2 // Imagine two skip block
   217  	blockRoot, err := block.Block.HashTreeRoot()
   218  	require.NoError(t, err, "Could not hash beacon block")
   219  	justifiedBlockRoot, err := justifiedBlock.Block.HashTreeRoot()
   220  	require.NoError(t, err, "Could not hash justified block")
   221  	epochBoundaryRoot, err := epochBoundaryBlock.Block.HashTreeRoot()
   222  	require.NoError(t, err, "Could not hash justified block")
   223  	slot := types.Slot(10000)
   224  
   225  	beaconState, err := testutil.NewBeaconState()
   226  	require.NoError(t, err)
   227  	require.NoError(t, beaconState.SetSlot(slot))
   228  	err = beaconState.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{
   229  		Epoch: helpers.SlotToEpoch(1500),
   230  		Root:  justifiedBlockRoot[:],
   231  	})
   232  	require.NoError(t, err)
   233  	blockRoots := beaconState.BlockRoots()
   234  	blockRoots[1] = blockRoot[:]
   235  	blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = epochBoundaryRoot[:]
   236  	blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedBlockRoot[:]
   237  	require.NoError(t, beaconState.SetBlockRoots(blockRoots))
   238  	chainService := &mock.ChainService{
   239  		Genesis: time.Now(),
   240  	}
   241  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   242  	attesterServer := &Server{
   243  		BeaconDB:         db,
   244  		P2P:              &mockp2p.MockBroadcaster{},
   245  		AttestationCache: cache.NewAttestationCache(),
   246  		HeadFetcher:      &mock.ChainService{State: beaconState, Root: blockRoot[:]},
   247  		FinalizationFetcher: &mock.ChainService{
   248  			CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint(),
   249  		},
   250  		SyncChecker:   &mockSync.Sync{IsSyncing: false},
   251  		TimeFetcher:   &mock.ChainService{Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second)},
   252  		StateNotifier: chainService.StateNotifier(),
   253  	}
   254  	require.NoError(t, db.SaveState(ctx, beaconState, blockRoot))
   255  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
   256  	require.NoError(t, db.SaveHeadBlockRoot(ctx, blockRoot))
   257  
   258  	req := &ethpb.AttestationDataRequest{
   259  		CommitteeIndex: 0,
   260  		Slot:           10000,
   261  	}
   262  	res, err := attesterServer.GetAttestationData(context.Background(), req)
   263  	require.NoError(t, err, "Could not get attestation info at slot")
   264  
   265  	expectedInfo := &ethpb.AttestationData{
   266  		Slot:            req.Slot,
   267  		BeaconBlockRoot: blockRoot[:],
   268  		Source: &ethpb.Checkpoint{
   269  			Epoch: helpers.SlotToEpoch(1500),
   270  			Root:  justifiedBlockRoot[:],
   271  		},
   272  		Target: &ethpb.Checkpoint{
   273  			Epoch: 312,
   274  			Root:  blockRoot[:],
   275  		},
   276  	}
   277  
   278  	if !proto.Equal(res, expectedInfo) {
   279  		t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
   280  	}
   281  }
   282  
   283  func TestAttestationDataSlot_handlesInProgressRequest(t *testing.T) {
   284  	s := &pbp2p.BeaconState{Slot: 100}
   285  	state, err := v1.InitializeFromProto(s)
   286  	require.NoError(t, err)
   287  	ctx := context.Background()
   288  	chainService := &mock.ChainService{
   289  		Genesis: time.Now(),
   290  	}
   291  	slot := types.Slot(2)
   292  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   293  	server := &Server{
   294  		HeadFetcher:      &mock.ChainService{State: state},
   295  		AttestationCache: cache.NewAttestationCache(),
   296  		SyncChecker:      &mockSync.Sync{IsSyncing: false},
   297  		TimeFetcher:      &mock.ChainService{Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second)},
   298  		StateNotifier:    chainService.StateNotifier(),
   299  	}
   300  
   301  	req := &ethpb.AttestationDataRequest{
   302  		CommitteeIndex: 1,
   303  		Slot:           slot,
   304  	}
   305  
   306  	res := &ethpb.AttestationData{
   307  		CommitteeIndex: 1,
   308  		Target:         &ethpb.Checkpoint{Epoch: 55, Root: make([]byte, 32)},
   309  	}
   310  
   311  	require.NoError(t, server.AttestationCache.MarkInProgress(req))
   312  
   313  	var wg sync.WaitGroup
   314  
   315  	wg.Add(1)
   316  	go func() {
   317  		defer wg.Done()
   318  		response, err := server.GetAttestationData(ctx, req)
   319  		require.NoError(t, err)
   320  		if !proto.Equal(res, response) {
   321  			t.Error("Expected  equal responses from cache")
   322  		}
   323  	}()
   324  
   325  	wg.Add(1)
   326  	go func() {
   327  		defer wg.Done()
   328  
   329  		assert.NoError(t, server.AttestationCache.Put(ctx, req, res))
   330  		assert.NoError(t, server.AttestationCache.MarkNotInProgress(req))
   331  	}()
   332  
   333  	wg.Wait()
   334  }
   335  
   336  func TestServer_GetAttestationData_InvalidRequestSlot(t *testing.T) {
   337  	ctx := context.Background()
   338  
   339  	slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
   340  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   341  	attesterServer := &Server{
   342  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   343  		TimeFetcher: &mock.ChainService{Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second)},
   344  	}
   345  
   346  	req := &ethpb.AttestationDataRequest{
   347  		Slot: 1000000000000,
   348  	}
   349  	_, err := attesterServer.GetAttestationData(ctx, req)
   350  	assert.ErrorContains(t, "invalid request", err)
   351  }
   352  
   353  func TestServer_GetAttestationData_HeadStateSlotGreaterThanRequestSlot(t *testing.T) {
   354  	// There exists a rare scenario where the validator may request an attestation for a slot less
   355  	// than the head state's slot. The Ethereum consensus spec constraints require the block root the
   356  	// attestation is referencing be less than or equal to the attestation data slot.
   357  	// See: https://github.com/prysmaticlabs/prysm/issues/5164
   358  	ctx := context.Background()
   359  	db := dbutil.SetupDB(t)
   360  
   361  	slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
   362  	block := testutil.NewBeaconBlock()
   363  	block.Block.Slot = slot
   364  	block2 := testutil.NewBeaconBlock()
   365  	block2.Block.Slot = slot - 1
   366  	targetBlock := testutil.NewBeaconBlock()
   367  	targetBlock.Block.Slot = 1 * params.BeaconConfig().SlotsPerEpoch
   368  	justifiedBlock := testutil.NewBeaconBlock()
   369  	justifiedBlock.Block.Slot = 2 * params.BeaconConfig().SlotsPerEpoch
   370  	blockRoot, err := block.Block.HashTreeRoot()
   371  	require.NoError(t, err, "Could not hash beacon block")
   372  	blockRoot2, err := block2.HashTreeRoot()
   373  	require.NoError(t, err)
   374  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block2)))
   375  	justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
   376  	require.NoError(t, err, "Could not get signing root for justified block")
   377  	targetRoot, err := targetBlock.Block.HashTreeRoot()
   378  	require.NoError(t, err, "Could not get signing root for target block")
   379  
   380  	beaconState, err := testutil.NewBeaconState()
   381  	require.NoError(t, err)
   382  	require.NoError(t, beaconState.SetSlot(slot))
   383  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   384  	require.NoError(t, beaconState.SetGenesisTime(uint64(time.Now().Unix()-offset)))
   385  	err = beaconState.SetLatestBlockHeader(testutil.HydrateBeaconHeader(&ethpb.BeaconBlockHeader{
   386  		ParentRoot: blockRoot2[:],
   387  	}))
   388  	require.NoError(t, err)
   389  	err = beaconState.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{
   390  		Epoch: 2,
   391  		Root:  justifiedRoot[:],
   392  	})
   393  	require.NoError(t, err)
   394  	blockRoots := beaconState.BlockRoots()
   395  	blockRoots[1] = blockRoot[:]
   396  	blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:]
   397  	blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:]
   398  	blockRoots[3*params.BeaconConfig().SlotsPerEpoch] = blockRoot2[:]
   399  	require.NoError(t, beaconState.SetBlockRoots(blockRoots))
   400  
   401  	beaconState2 := beaconState.Copy()
   402  	require.NoError(t, beaconState2.SetSlot(beaconState2.Slot()-1))
   403  	require.NoError(t, db.SaveState(ctx, beaconState2, blockRoot2))
   404  	chainService := &mock.ChainService{
   405  		Genesis: time.Now(),
   406  	}
   407  	offset = int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   408  	attesterServer := &Server{
   409  		BeaconDB:            db,
   410  		P2P:                 &mockp2p.MockBroadcaster{},
   411  		SyncChecker:         &mockSync.Sync{IsSyncing: false},
   412  		AttestationCache:    cache.NewAttestationCache(),
   413  		HeadFetcher:         &mock.ChainService{State: beaconState, Root: blockRoot[:]},
   414  		FinalizationFetcher: &mock.ChainService{CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint()},
   415  		TimeFetcher:         &mock.ChainService{Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second)},
   416  		StateNotifier:       chainService.StateNotifier(),
   417  		StateGen:            stategen.New(db),
   418  	}
   419  	require.NoError(t, db.SaveState(ctx, beaconState, blockRoot))
   420  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
   421  	require.NoError(t, db.SaveHeadBlockRoot(ctx, blockRoot))
   422  
   423  	req := &ethpb.AttestationDataRequest{
   424  		CommitteeIndex: 0,
   425  		Slot:           slot - 1,
   426  	}
   427  	res, err := attesterServer.GetAttestationData(ctx, req)
   428  	require.NoError(t, err, "Could not get attestation info at slot")
   429  
   430  	expectedInfo := &ethpb.AttestationData{
   431  		Slot:            slot - 1,
   432  		BeaconBlockRoot: blockRoot2[:],
   433  		Source: &ethpb.Checkpoint{
   434  			Epoch: 2,
   435  			Root:  justifiedRoot[:],
   436  		},
   437  		Target: &ethpb.Checkpoint{
   438  			Epoch: 3,
   439  			Root:  blockRoot2[:],
   440  		},
   441  	}
   442  
   443  	if !proto.Equal(res, expectedInfo) {
   444  		t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
   445  	}
   446  }
   447  
   448  func TestGetAttestationData_SucceedsInFirstEpoch(t *testing.T) {
   449  	ctx := context.Background()
   450  	db := dbutil.SetupDB(t)
   451  
   452  	slot := types.Slot(5)
   453  	block := testutil.NewBeaconBlock()
   454  	block.Block.Slot = slot
   455  	targetBlock := testutil.NewBeaconBlock()
   456  	targetBlock.Block.Slot = 0
   457  	justifiedBlock := testutil.NewBeaconBlock()
   458  	justifiedBlock.Block.Slot = 0
   459  	blockRoot, err := block.Block.HashTreeRoot()
   460  	require.NoError(t, err, "Could not hash beacon block")
   461  	justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
   462  	require.NoError(t, err, "Could not get signing root for justified block")
   463  	targetRoot, err := targetBlock.Block.HashTreeRoot()
   464  	require.NoError(t, err, "Could not get signing root for target block")
   465  
   466  	beaconState, err := testutil.NewBeaconState()
   467  	require.NoError(t, err)
   468  	require.NoError(t, beaconState.SetSlot(slot))
   469  	err = beaconState.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{
   470  		Epoch: 0,
   471  		Root:  justifiedRoot[:],
   472  	})
   473  	require.NoError(t, err)
   474  	blockRoots := beaconState.BlockRoots()
   475  	blockRoots[1] = blockRoot[:]
   476  	blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:]
   477  	blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:]
   478  	require.NoError(t, beaconState.SetBlockRoots(blockRoots))
   479  	chainService := &mock.ChainService{
   480  		Genesis: time.Now(),
   481  	}
   482  	offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
   483  	attesterServer := &Server{
   484  		BeaconDB:         db,
   485  		P2P:              &mockp2p.MockBroadcaster{},
   486  		SyncChecker:      &mockSync.Sync{IsSyncing: false},
   487  		AttestationCache: cache.NewAttestationCache(),
   488  		HeadFetcher: &mock.ChainService{
   489  			State: beaconState, Root: blockRoot[:],
   490  		},
   491  		FinalizationFetcher: &mock.ChainService{
   492  			CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint(),
   493  		},
   494  		TimeFetcher:   &mock.ChainService{Genesis: timeutils.Now().Add(time.Duration(-1*offset) * time.Second)},
   495  		StateNotifier: chainService.StateNotifier(),
   496  	}
   497  	require.NoError(t, db.SaveState(ctx, beaconState, blockRoot))
   498  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
   499  	require.NoError(t, db.SaveHeadBlockRoot(ctx, blockRoot))
   500  
   501  	req := &ethpb.AttestationDataRequest{
   502  		CommitteeIndex: 0,
   503  		Slot:           5,
   504  	}
   505  	res, err := attesterServer.GetAttestationData(context.Background(), req)
   506  	require.NoError(t, err, "Could not get attestation info at slot")
   507  
   508  	expectedInfo := &ethpb.AttestationData{
   509  		Slot:            slot,
   510  		BeaconBlockRoot: blockRoot[:],
   511  		Source: &ethpb.Checkpoint{
   512  			Epoch: 0,
   513  			Root:  justifiedRoot[:],
   514  		},
   515  		Target: &ethpb.Checkpoint{
   516  			Epoch: 0,
   517  			Root:  blockRoot[:],
   518  		},
   519  	}
   520  
   521  	if !proto.Equal(res, expectedInfo) {
   522  		t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
   523  	}
   524  }
   525  
   526  func TestServer_SubscribeCommitteeSubnets_NoSlots(t *testing.T) {
   527  	db := dbutil.SetupDB(t)
   528  
   529  	attesterServer := &Server{
   530  		HeadFetcher:       &mock.ChainService{},
   531  		P2P:               &mockp2p.MockBroadcaster{},
   532  		BeaconDB:          db,
   533  		AttestationCache:  cache.NewAttestationCache(),
   534  		AttPool:           attestations.NewPool(),
   535  		OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
   536  	}
   537  
   538  	_, err := attesterServer.SubscribeCommitteeSubnets(context.Background(), &ethpb.CommitteeSubnetsSubscribeRequest{
   539  		Slots:        nil,
   540  		CommitteeIds: nil,
   541  		IsAggregator: nil,
   542  	})
   543  	assert.ErrorContains(t, "no attester slots provided", err)
   544  }
   545  
   546  func TestServer_SubscribeCommitteeSubnets_DifferentLengthSlots(t *testing.T) {
   547  	db := dbutil.SetupDB(t)
   548  
   549  	// fixed seed
   550  	s := rand.NewSource(10)
   551  	randGen := rand.New(s)
   552  
   553  	attesterServer := &Server{
   554  		HeadFetcher:       &mock.ChainService{},
   555  		P2P:               &mockp2p.MockBroadcaster{},
   556  		BeaconDB:          db,
   557  		AttestationCache:  cache.NewAttestationCache(),
   558  		AttPool:           attestations.NewPool(),
   559  		OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
   560  	}
   561  
   562  	var slots []types.Slot
   563  	var comIdxs []types.CommitteeIndex
   564  	var isAggregator []bool
   565  
   566  	for i := types.Slot(100); i < 200; i++ {
   567  		slots = append(slots, i)
   568  		comIdxs = append(comIdxs, types.CommitteeIndex(randGen.Int63n(64)))
   569  		boolVal := randGen.Uint64()%2 == 0
   570  		isAggregator = append(isAggregator, boolVal)
   571  	}
   572  
   573  	slots = append(slots, 321)
   574  
   575  	_, err := attesterServer.SubscribeCommitteeSubnets(context.Background(), &ethpb.CommitteeSubnetsSubscribeRequest{
   576  		Slots:        slots,
   577  		CommitteeIds: comIdxs,
   578  		IsAggregator: isAggregator,
   579  	})
   580  	assert.ErrorContains(t, "request fields are not the same length", err)
   581  }
   582  
   583  func TestServer_SubscribeCommitteeSubnets_MultipleSlots(t *testing.T) {
   584  	db := dbutil.SetupDB(t)
   585  	// fixed seed
   586  	s := rand.NewSource(10)
   587  	randGen := rand.New(s)
   588  
   589  	validators := make([]*ethpb.Validator, 64)
   590  	for i := 0; i < len(validators); i++ {
   591  		validators[i] = &ethpb.Validator{
   592  			ExitEpoch:        params.BeaconConfig().FarFutureEpoch,
   593  			EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
   594  		}
   595  	}
   596  
   597  	state, err := testutil.NewBeaconState()
   598  	require.NoError(t, err)
   599  	require.NoError(t, state.SetValidators(validators))
   600  
   601  	attesterServer := &Server{
   602  		HeadFetcher:       &mock.ChainService{State: state},
   603  		P2P:               &mockp2p.MockBroadcaster{},
   604  		BeaconDB:          db,
   605  		AttestationCache:  cache.NewAttestationCache(),
   606  		AttPool:           attestations.NewPool(),
   607  		OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
   608  	}
   609  
   610  	var slots []types.Slot
   611  	var comIdxs []types.CommitteeIndex
   612  	var isAggregator []bool
   613  
   614  	for i := types.Slot(100); i < 200; i++ {
   615  		slots = append(slots, i)
   616  		comIdxs = append(comIdxs, types.CommitteeIndex(randGen.Int63n(64)))
   617  		boolVal := randGen.Uint64()%2 == 0
   618  		isAggregator = append(isAggregator, boolVal)
   619  	}
   620  
   621  	_, err = attesterServer.SubscribeCommitteeSubnets(context.Background(), &ethpb.CommitteeSubnetsSubscribeRequest{
   622  		Slots:        slots,
   623  		CommitteeIds: comIdxs,
   624  		IsAggregator: isAggregator,
   625  	})
   626  	require.NoError(t, err)
   627  	for i := types.Slot(100); i < 200; i++ {
   628  		subnets := cache.SubnetIDs.GetAttesterSubnetIDs(i)
   629  		assert.Equal(t, 1, len(subnets))
   630  		if isAggregator[i-100] {
   631  			subnets = cache.SubnetIDs.GetAggregatorSubnetIDs(i)
   632  			assert.Equal(t, 1, len(subnets))
   633  		}
   634  	}
   635  }