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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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(ðpb.Attestation{ 227 Data: ðpb.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(ðpb.Attestation{ 264 Data: ðpb.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 := ðpb.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 := ðpb.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 := ðpb.SignedAggregateSubmitRequest{ 411 SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProof{ 412 Signature: make([]byte, params.BeaconConfig().BLSSignatureLength), 413 Message: ðpb.AggregateAttestationAndProof{ 414 Aggregate: ðpb.Attestation{ 415 Data: ðpb.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 = ðpb.SignedAggregateSubmitRequest{ 424 SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProof{ 425 Signature: []byte{'a'}, 426 Message: ðpb.AggregateAttestationAndProof{ 427 Aggregate: ðpb.Attestation{ 428 Data: ðpb.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 := ðpb.SignedAggregateSubmitRequest{ 442 SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProof{ 443 Signature: []byte{'a'}, 444 Message: ðpb.AggregateAttestationAndProof{ 445 SelectionProof: []byte{'a'}, 446 Aggregate: ðpb.Attestation{ 447 Data: ðpb.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 }