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

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/prysmaticlabs/go-bitfield"
    10  	mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    13  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
    14  	mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    15  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    16  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    17  	mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
    18  	pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    19  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    20  	attaggregation "github.com/prysmaticlabs/prysm/shared/aggregation/attestations"
    21  	"github.com/prysmaticlabs/prysm/shared/attestationutil"
    22  	"github.com/prysmaticlabs/prysm/shared/bls"
    23  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    24  	"github.com/prysmaticlabs/prysm/shared/params"
    25  	"github.com/prysmaticlabs/prysm/shared/testutil"
    26  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    27  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    28  )
    29  
    30  func TestSubmitAggregateAndProof_Syncing(t *testing.T) {
    31  	db := dbutil.SetupDB(t)
    32  	ctx := context.Background()
    33  
    34  	s := &v1.BeaconState{}
    35  
    36  	aggregatorServer := &Server{
    37  		HeadFetcher: &mock.ChainService{State: s},
    38  		SyncChecker: &mockSync.Sync{IsSyncing: true},
    39  		BeaconDB:    db,
    40  	}
    41  
    42  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1}
    43  	wanted := "Syncing to latest head, not ready to respond"
    44  	_, err := aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
    45  	assert.ErrorContains(t, wanted, err)
    46  }
    47  
    48  func TestSubmitAggregateAndProof_CantFindValidatorIndex(t *testing.T) {
    49  	db := dbutil.SetupDB(t)
    50  	ctx := context.Background()
    51  
    52  	s, err := v1.InitializeFromProto(&pbp2p.BeaconState{
    53  		RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
    54  	})
    55  	require.NoError(t, err)
    56  
    57  	server := &Server{
    58  		HeadFetcher: &mock.ChainService{State: s},
    59  		SyncChecker: &mockSync.Sync{IsSyncing: false},
    60  		BeaconDB:    db,
    61  	}
    62  
    63  	priv, err := bls.RandKey()
    64  	require.NoError(t, err)
    65  	sig := priv.Sign([]byte{'A'})
    66  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey(3)}
    67  	wanted := "Could not locate validator index in DB"
    68  	_, err = server.SubmitAggregateSelectionProof(ctx, req)
    69  	assert.ErrorContains(t, wanted, err)
    70  }
    71  
    72  func TestSubmitAggregateAndProof_IsAggregatorAndNoAtts(t *testing.T) {
    73  	db := dbutil.SetupDB(t)
    74  	ctx := context.Background()
    75  
    76  	s, err := v1.InitializeFromProto(&pbp2p.BeaconState{
    77  		RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
    78  		Validators: []*ethpb.Validator{
    79  			{PublicKey: pubKey(0)},
    80  			{PublicKey: pubKey(1)},
    81  		},
    82  	})
    83  	require.NoError(t, err)
    84  
    85  	server := &Server{
    86  		HeadFetcher: &mock.ChainService{State: s},
    87  		SyncChecker: &mockSync.Sync{IsSyncing: false},
    88  		BeaconDB:    db,
    89  		AttPool:     attestations.NewPool(),
    90  	}
    91  
    92  	priv, err := bls.RandKey()
    93  	require.NoError(t, err)
    94  	sig := priv.Sign([]byte{'A'})
    95  	v, err := s.ValidatorAtIndex(1)
    96  	require.NoError(t, err)
    97  	pubKey := v.PublicKey
    98  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
    99  
   100  	_, err = server.SubmitAggregateSelectionProof(ctx, req)
   101  	assert.ErrorContains(t, "Could not find attestation for slot and committee in pool", err)
   102  }
   103  
   104  func TestSubmitAggregateAndProof_UnaggregateOk(t *testing.T) {
   105  	params.SetupTestConfigCleanup(t)
   106  	c := params.MinimalSpecConfig()
   107  	c.TargetAggregatorsPerCommittee = 16
   108  	params.OverrideBeaconConfig(c)
   109  
   110  	db := dbutil.SetupDB(t)
   111  	ctx := context.Background()
   112  
   113  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 32)
   114  	att0, err := generateUnaggregatedAtt(beaconState, 0, privKeys)
   115  	require.NoError(t, err)
   116  	err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
   117  	require.NoError(t, err)
   118  
   119  	aggregatorServer := &Server{
   120  		HeadFetcher: &mock.ChainService{State: beaconState},
   121  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   122  		BeaconDB:    db,
   123  		AttPool:     attestations.NewPool(),
   124  		P2P:         &mockp2p.MockBroadcaster{},
   125  	}
   126  
   127  	priv, err := bls.RandKey()
   128  	require.NoError(t, err)
   129  	sig := priv.Sign([]byte{'B'})
   130  	v, err := beaconState.ValidatorAtIndex(1)
   131  	require.NoError(t, err)
   132  	pubKey := v.PublicKey
   133  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
   134  
   135  	require.NoError(t, aggregatorServer.AttPool.SaveUnaggregatedAttestation(att0))
   136  	_, err = aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
   137  	require.NoError(t, err)
   138  }
   139  
   140  func TestSubmitAggregateAndProof_AggregateOk(t *testing.T) {
   141  	params.SetupTestConfigCleanup(t)
   142  	c := params.MinimalSpecConfig()
   143  	c.TargetAggregatorsPerCommittee = 16
   144  	params.OverrideBeaconConfig(c)
   145  
   146  	db := dbutil.SetupDB(t)
   147  	ctx := context.Background()
   148  
   149  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 32)
   150  	att0, err := generateAtt(beaconState, 0, privKeys)
   151  	require.NoError(t, err)
   152  	att1, err := generateAtt(beaconState, 2, privKeys)
   153  	require.NoError(t, err)
   154  
   155  	err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
   156  	require.NoError(t, err)
   157  
   158  	aggregatorServer := &Server{
   159  		HeadFetcher: &mock.ChainService{State: beaconState},
   160  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   161  		BeaconDB:    db,
   162  		AttPool:     attestations.NewPool(),
   163  		P2P:         &mockp2p.MockBroadcaster{},
   164  	}
   165  
   166  	priv, err := bls.RandKey()
   167  	require.NoError(t, err)
   168  	sig := priv.Sign([]byte{'B'})
   169  	v, err := beaconState.ValidatorAtIndex(1)
   170  	require.NoError(t, err)
   171  	pubKey := v.PublicKey
   172  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
   173  
   174  	require.NoError(t, aggregatorServer.AttPool.SaveAggregatedAttestation(att0))
   175  	require.NoError(t, aggregatorServer.AttPool.SaveAggregatedAttestation(att1))
   176  	_, err = aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
   177  	require.NoError(t, err)
   178  
   179  	aggregatedAtts := aggregatorServer.AttPool.AggregatedAttestations()
   180  	wanted, err := attaggregation.AggregatePair(att0, att1)
   181  	require.NoError(t, err)
   182  	if reflect.DeepEqual(aggregatedAtts, wanted) {
   183  		t.Error("Did not receive wanted attestation")
   184  	}
   185  }
   186  
   187  func TestSubmitAggregateAndProof_AggregateNotOk(t *testing.T) {
   188  	params.SetupTestConfigCleanup(t)
   189  	c := params.MinimalSpecConfig()
   190  	c.TargetAggregatorsPerCommittee = 16
   191  	params.OverrideBeaconConfig(c)
   192  
   193  	db := dbutil.SetupDB(t)
   194  	ctx := context.Background()
   195  
   196  	beaconState, _ := testutil.DeterministicGenesisState(t, 32)
   197  	require.NoError(t, beaconState.SetSlot(beaconState.Slot()+params.BeaconConfig().MinAttestationInclusionDelay))
   198  
   199  	aggregatorServer := &Server{
   200  		HeadFetcher: &mock.ChainService{State: beaconState},
   201  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   202  		BeaconDB:    db,
   203  		AttPool:     attestations.NewPool(),
   204  		P2P:         &mockp2p.MockBroadcaster{},
   205  	}
   206  
   207  	priv, err := bls.RandKey()
   208  	require.NoError(t, err)
   209  	sig := priv.Sign([]byte{'B'})
   210  	v, err := beaconState.ValidatorAtIndex(1)
   211  	require.NoError(t, err)
   212  	pubKey := v.PublicKey
   213  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
   214  
   215  	_, err = aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
   216  	assert.ErrorContains(t, "Could not find attestation for slot and committee in pool", err)
   217  
   218  	aggregatedAtts := aggregatorServer.AttPool.AggregatedAttestations()
   219  	assert.Equal(t, 0, len(aggregatedAtts), "Wanted aggregated attestation")
   220  }
   221  
   222  func generateAtt(state iface.ReadOnlyBeaconState, index uint64, privKeys []bls.SecretKey) (*ethpb.Attestation, error) {
   223  	aggBits := bitfield.NewBitlist(4)
   224  	aggBits.SetBitAt(index, true)
   225  	aggBits.SetBitAt(index+1, true)
   226  	att := testutil.HydrateAttestation(&ethpb.Attestation{
   227  		Data:            &ethpb.AttestationData{CommitteeIndex: 1},
   228  		AggregationBits: aggBits,
   229  	})
   230  	committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	sigs := make([]bls.Signature, len(attestingIndices))
   240  	zeroSig := [96]byte{}
   241  	att.Signature = zeroSig[:]
   242  
   243  	for i, indice := range attestingIndices {
   244  		sb, err := helpers.ComputeDomainAndSign(state, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice])
   245  		if err != nil {
   246  			return nil, err
   247  		}
   248  		sig, err := bls.SignatureFromBytes(sb)
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  		sigs[i] = sig
   253  	}
   254  
   255  	att.Signature = bls.AggregateSignatures(sigs).Marshal()
   256  
   257  	return att, nil
   258  }
   259  
   260  func generateUnaggregatedAtt(state iface.ReadOnlyBeaconState, index uint64, privKeys []bls.SecretKey) (*ethpb.Attestation, error) {
   261  	aggBits := bitfield.NewBitlist(4)
   262  	aggBits.SetBitAt(index, true)
   263  	att := testutil.HydrateAttestation(&ethpb.Attestation{
   264  		Data: &ethpb.AttestationData{
   265  			CommitteeIndex: 1,
   266  		},
   267  		AggregationBits: aggBits,
   268  	})
   269  	committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	domain, err := helpers.Domain(state.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, params.BeaconConfig().ZeroHash[:])
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	sigs := make([]bls.Signature, len(attestingIndices))
   283  	zeroSig := [96]byte{}
   284  	att.Signature = zeroSig[:]
   285  
   286  	for i, indice := range attestingIndices {
   287  		hashTreeRoot, err := helpers.ComputeSigningRoot(att.Data, domain)
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  		sig := privKeys[indice].Sign(hashTreeRoot[:])
   292  		sigs[i] = sig
   293  	}
   294  
   295  	att.Signature = bls.AggregateSignatures(sigs).Marshal()
   296  
   297  	return att, nil
   298  }
   299  
   300  func TestSubmitAggregateAndProof_PreferOwnAttestation(t *testing.T) {
   301  	params.SetupTestConfigCleanup(t)
   302  	c := params.MinimalSpecConfig()
   303  	c.TargetAggregatorsPerCommittee = 16
   304  	params.OverrideBeaconConfig(c)
   305  
   306  	db := dbutil.SetupDB(t)
   307  	ctx := context.Background()
   308  
   309  	// This test creates 3 attestations. 0 and 2 have the same attestation data and can be
   310  	// aggregated. 1 has the validator's signature making this request and that is the expected
   311  	// attestation to sign, even though the aggregated 0&2 would have more aggregated bits.
   312  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 32)
   313  	att0, err := generateAtt(beaconState, 0, privKeys)
   314  	require.NoError(t, err)
   315  	att0.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("foo"), 32)
   316  	att0.AggregationBits = bitfield.Bitlist{0b11100}
   317  	att1, err := generateAtt(beaconState, 0, privKeys)
   318  	require.NoError(t, err)
   319  	att1.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("bar"), 32)
   320  	att1.AggregationBits = bitfield.Bitlist{0b11001}
   321  	att2, err := generateAtt(beaconState, 2, privKeys)
   322  	require.NoError(t, err)
   323  	att2.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("foo"), 32)
   324  	att2.AggregationBits = bitfield.Bitlist{0b11110}
   325  
   326  	err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
   327  	require.NoError(t, err)
   328  
   329  	aggregatorServer := &Server{
   330  		HeadFetcher: &mock.ChainService{State: beaconState},
   331  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   332  		BeaconDB:    db,
   333  		AttPool:     attestations.NewPool(),
   334  		P2P:         &mockp2p.MockBroadcaster{},
   335  	}
   336  
   337  	priv, err := bls.RandKey()
   338  	require.NoError(t, err)
   339  	sig := priv.Sign([]byte{'B'})
   340  	v, err := beaconState.ValidatorAtIndex(1)
   341  	require.NoError(t, err)
   342  	pubKey := v.PublicKey
   343  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
   344  
   345  	err = aggregatorServer.AttPool.SaveAggregatedAttestations([]*ethpb.Attestation{
   346  		att0,
   347  		att1,
   348  		att2,
   349  	})
   350  	require.NoError(t, err)
   351  
   352  	res, err := aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
   353  	require.NoError(t, err)
   354  	assert.DeepSSZEqual(t, att1, res.AggregateAndProof.Aggregate, "Did not receive wanted attestation")
   355  }
   356  
   357  func TestSubmitAggregateAndProof_SelectsMostBitsWhenOwnAttestationNotPresent(t *testing.T) {
   358  	params.SetupTestConfigCleanup(t)
   359  	c := params.MinimalSpecConfig()
   360  	c.TargetAggregatorsPerCommittee = 16
   361  	params.OverrideBeaconConfig(c)
   362  
   363  	db := dbutil.SetupDB(t)
   364  	ctx := context.Background()
   365  
   366  	// This test creates two distinct attestations, neither of which contain the validator's index,
   367  	// index 0. This test should choose the most bits attestation, att1.
   368  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 32)
   369  	att0, err := generateAtt(beaconState, 0, privKeys)
   370  	require.NoError(t, err)
   371  	att0.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("foo"), 32)
   372  	att0.AggregationBits = bitfield.Bitlist{0b11100}
   373  	att1, err := generateAtt(beaconState, 2, privKeys)
   374  	require.NoError(t, err)
   375  	att1.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("bar"), 32)
   376  	att1.AggregationBits = bitfield.Bitlist{0b11110}
   377  
   378  	err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
   379  	require.NoError(t, err)
   380  
   381  	aggregatorServer := &Server{
   382  		HeadFetcher: &mock.ChainService{State: beaconState},
   383  		SyncChecker: &mockSync.Sync{IsSyncing: false},
   384  		BeaconDB:    db,
   385  		AttPool:     attestations.NewPool(),
   386  		P2P:         &mockp2p.MockBroadcaster{},
   387  	}
   388  
   389  	priv, err := bls.RandKey()
   390  	require.NoError(t, err)
   391  	sig := priv.Sign([]byte{'B'})
   392  	v, err := beaconState.ValidatorAtIndex(1)
   393  	require.NoError(t, err)
   394  	pubKey := v.PublicKey
   395  	req := &ethpb.AggregateSelectionRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey}
   396  
   397  	err = aggregatorServer.AttPool.SaveAggregatedAttestations([]*ethpb.Attestation{
   398  		att0,
   399  		att1,
   400  	})
   401  	require.NoError(t, err)
   402  
   403  	res, err := aggregatorServer.SubmitAggregateSelectionProof(ctx, req)
   404  	require.NoError(t, err)
   405  	assert.DeepSSZEqual(t, att1, res.AggregateAndProof.Aggregate, "Did not receive wanted attestation")
   406  }
   407  
   408  func TestSubmitSignedAggregateSelectionProof_ZeroHashesSignatures(t *testing.T) {
   409  	aggregatorServer := &Server{}
   410  	req := &ethpb.SignedAggregateSubmitRequest{
   411  		SignedAggregateAndProof: &ethpb.SignedAggregateAttestationAndProof{
   412  			Signature: make([]byte, params.BeaconConfig().BLSSignatureLength),
   413  			Message: &ethpb.AggregateAttestationAndProof{
   414  				Aggregate: &ethpb.Attestation{
   415  					Data: &ethpb.AttestationData{},
   416  				},
   417  			},
   418  		},
   419  	}
   420  	_, err := aggregatorServer.SubmitSignedAggregateSelectionProof(context.Background(), req)
   421  	require.ErrorContains(t, "Signed signatures can't be zero hashes", err)
   422  
   423  	req = &ethpb.SignedAggregateSubmitRequest{
   424  		SignedAggregateAndProof: &ethpb.SignedAggregateAttestationAndProof{
   425  			Signature: []byte{'a'},
   426  			Message: &ethpb.AggregateAttestationAndProof{
   427  				Aggregate: &ethpb.Attestation{
   428  					Data: &ethpb.AttestationData{},
   429  				},
   430  				SelectionProof: make([]byte, params.BeaconConfig().BLSSignatureLength),
   431  			},
   432  		},
   433  	}
   434  	_, err = aggregatorServer.SubmitSignedAggregateSelectionProof(context.Background(), req)
   435  	require.ErrorContains(t, "Signed signatures can't be zero hashes", err)
   436  }
   437  
   438  func TestSubmitSignedAggregateSelectionProof_InvalidSlot(t *testing.T) {
   439  	c := &mock.ChainService{Genesis: time.Now()}
   440  	aggregatorServer := &Server{TimeFetcher: c}
   441  	req := &ethpb.SignedAggregateSubmitRequest{
   442  		SignedAggregateAndProof: &ethpb.SignedAggregateAttestationAndProof{
   443  			Signature: []byte{'a'},
   444  			Message: &ethpb.AggregateAttestationAndProof{
   445  				SelectionProof: []byte{'a'},
   446  				Aggregate: &ethpb.Attestation{
   447  					Data: &ethpb.AttestationData{Slot: 1000},
   448  				},
   449  			},
   450  		},
   451  	}
   452  	_, err := aggregatorServer.SubmitSignedAggregateSelectionProof(context.Background(), req)
   453  	require.ErrorContains(t, "Attestation slot is no longer valid from current time", err)
   454  }