github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/beacon/attestations_test.go (about) 1 package beacon 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/golang/mock/gomock" 12 types "github.com/prysmaticlabs/eth2-types" 13 "github.com/prysmaticlabs/go-bitfield" 14 chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 15 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 16 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" 17 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 18 dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 19 "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" 20 "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" 21 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 22 pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 23 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 24 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 25 "github.com/prysmaticlabs/prysm/proto/interfaces" 26 attaggregation "github.com/prysmaticlabs/prysm/shared/aggregation/attestations" 27 "github.com/prysmaticlabs/prysm/shared/attestationutil" 28 "github.com/prysmaticlabs/prysm/shared/bytesutil" 29 "github.com/prysmaticlabs/prysm/shared/cmd" 30 "github.com/prysmaticlabs/prysm/shared/mock" 31 "github.com/prysmaticlabs/prysm/shared/params" 32 "github.com/prysmaticlabs/prysm/shared/testutil" 33 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 34 "github.com/prysmaticlabs/prysm/shared/testutil/require" 35 "google.golang.org/protobuf/proto" 36 "google.golang.org/protobuf/types/known/emptypb" 37 ) 38 39 func TestServer_ListAttestations_NoResults(t *testing.T) { 40 db := dbTest.SetupDB(t) 41 ctx := context.Background() 42 43 st, err := v1.InitializeFromProto(&pbp2p.BeaconState{ 44 Slot: 0, 45 }) 46 require.NoError(t, err) 47 bs := &Server{ 48 BeaconDB: db, 49 HeadFetcher: &chainMock.ChainService{ 50 State: st, 51 }, 52 } 53 wanted := ðpb.ListAttestationsResponse{ 54 Attestations: make([]*ethpb.Attestation, 0), 55 TotalSize: int32(0), 56 NextPageToken: strconv.Itoa(0), 57 } 58 res, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ 59 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{GenesisEpoch: true}, 60 }) 61 require.NoError(t, err) 62 if !proto.Equal(wanted, res) { 63 t.Errorf("Wanted %v, received %v", wanted, res) 64 } 65 } 66 67 func TestServer_ListAttestations_Genesis(t *testing.T) { 68 db := dbTest.SetupDB(t) 69 ctx := context.Background() 70 71 st, err := v1.InitializeFromProto(&pbp2p.BeaconState{ 72 Slot: 0, 73 }) 74 require.NoError(t, err) 75 bs := &Server{ 76 BeaconDB: db, 77 HeadFetcher: &chainMock.ChainService{ 78 State: st, 79 }, 80 } 81 82 att := testutil.HydrateAttestation(ðpb.Attestation{ 83 AggregationBits: bitfield.NewBitlist(0), 84 Data: ðpb.AttestationData{ 85 Slot: 2, 86 CommitteeIndex: 1, 87 }, 88 }) 89 90 parentRoot := [32]byte{1, 2, 3} 91 signedBlock := testutil.NewBeaconBlock() 92 signedBlock.Block.ParentRoot = bytesutil.PadTo(parentRoot[:], 32) 93 signedBlock.Block.Body.Attestations = []*ethpb.Attestation{att} 94 root, err := signedBlock.Block.HashTreeRoot() 95 require.NoError(t, err) 96 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(signedBlock))) 97 require.NoError(t, db.SaveGenesisBlockRoot(ctx, root)) 98 wanted := ðpb.ListAttestationsResponse{ 99 Attestations: []*ethpb.Attestation{att}, 100 NextPageToken: "", 101 TotalSize: 1, 102 } 103 104 res, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ 105 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 106 GenesisEpoch: true, 107 }, 108 }) 109 require.NoError(t, err) 110 require.DeepSSZEqual(t, wanted, res) 111 } 112 113 func TestServer_ListAttestations_NoPagination(t *testing.T) { 114 db := dbTest.SetupDB(t) 115 ctx := context.Background() 116 117 count := types.Slot(8) 118 atts := make([]*ethpb.Attestation, 0, count) 119 for i := types.Slot(0); i < count; i++ { 120 blockExample := testutil.NewBeaconBlock() 121 blockExample.Block.Body.Attestations = []*ethpb.Attestation{ 122 { 123 Signature: make([]byte, 96), 124 Data: ðpb.AttestationData{ 125 Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("root"), 32)}, 126 Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("root"), 32)}, 127 BeaconBlockRoot: bytesutil.PadTo([]byte("root"), 32), 128 Slot: i, 129 }, 130 AggregationBits: bitfield.Bitlist{0b11}, 131 }, 132 } 133 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 134 atts = append(atts, blockExample.Block.Body.Attestations...) 135 } 136 137 bs := &Server{ 138 BeaconDB: db, 139 } 140 141 received, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ 142 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 143 GenesisEpoch: true, 144 }, 145 }) 146 require.NoError(t, err) 147 require.DeepEqual(t, atts, received.Attestations, "Incorrect attestations response") 148 } 149 150 func TestServer_ListAttestations_FiltersCorrectly(t *testing.T) { 151 db := dbTest.SetupDB(t) 152 ctx := context.Background() 153 154 someRoot := [32]byte{1, 2, 3} 155 sourceRoot := [32]byte{4, 5, 6} 156 sourceEpoch := types.Epoch(5) 157 targetRoot := [32]byte{7, 8, 9} 158 targetEpoch := types.Epoch(7) 159 160 blocks := []interfaces.SignedBeaconBlock{ 161 wrapper.WrappedPhase0SignedBeaconBlock( 162 testutil.HydrateSignedBeaconBlock( 163 ðpb.SignedBeaconBlock{ 164 Block: ðpb.BeaconBlock{ 165 Slot: 4, 166 Body: ðpb.BeaconBlockBody{ 167 Attestations: []*ethpb.Attestation{ 168 { 169 Data: ðpb.AttestationData{ 170 BeaconBlockRoot: someRoot[:], 171 Source: ðpb.Checkpoint{ 172 Root: sourceRoot[:], 173 Epoch: sourceEpoch, 174 }, 175 Target: ðpb.Checkpoint{ 176 Root: targetRoot[:], 177 Epoch: targetEpoch, 178 }, 179 Slot: 3, 180 }, 181 AggregationBits: bitfield.Bitlist{0b11}, 182 Signature: bytesutil.PadTo([]byte("sig"), 96), 183 }, 184 }, 185 }, 186 }, 187 })), 188 wrapper.WrappedPhase0SignedBeaconBlock( 189 testutil.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{ 190 Block: ðpb.BeaconBlock{ 191 Slot: 5 + params.BeaconConfig().SlotsPerEpoch, 192 Body: ðpb.BeaconBlockBody{ 193 Attestations: []*ethpb.Attestation{ 194 { 195 Data: ðpb.AttestationData{ 196 BeaconBlockRoot: someRoot[:], 197 Source: ðpb.Checkpoint{ 198 Root: sourceRoot[:], 199 Epoch: sourceEpoch, 200 }, 201 Target: ðpb.Checkpoint{ 202 Root: targetRoot[:], 203 Epoch: targetEpoch, 204 }, 205 Slot: 4 + params.BeaconConfig().SlotsPerEpoch, 206 }, 207 AggregationBits: bitfield.Bitlist{0b11}, 208 Signature: bytesutil.PadTo([]byte("sig"), 96), 209 }, 210 }, 211 }, 212 }, 213 })), 214 wrapper.WrappedPhase0SignedBeaconBlock( 215 testutil.HydrateSignedBeaconBlock( 216 ðpb.SignedBeaconBlock{ 217 Block: ðpb.BeaconBlock{ 218 Slot: 5, 219 Body: ðpb.BeaconBlockBody{ 220 Attestations: []*ethpb.Attestation{ 221 { 222 Data: ðpb.AttestationData{ 223 BeaconBlockRoot: someRoot[:], 224 Source: ðpb.Checkpoint{ 225 Root: sourceRoot[:], 226 Epoch: sourceEpoch, 227 }, 228 Target: ðpb.Checkpoint{ 229 Root: targetRoot[:], 230 Epoch: targetEpoch, 231 }, 232 Slot: 4, 233 }, 234 AggregationBits: bitfield.Bitlist{0b11}, 235 Signature: bytesutil.PadTo([]byte("sig"), 96), 236 }, 237 }, 238 }, 239 }, 240 })), 241 } 242 243 require.NoError(t, db.SaveBlocks(ctx, blocks)) 244 245 bs := &Server{ 246 BeaconDB: db, 247 } 248 249 received, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ 250 QueryFilter: ðpb.ListAttestationsRequest_Epoch{Epoch: 1}, 251 }) 252 require.NoError(t, err) 253 assert.Equal(t, 1, len(received.Attestations)) 254 received, err = bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ 255 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{GenesisEpoch: true}, 256 }) 257 require.NoError(t, err) 258 assert.Equal(t, 2, len(received.Attestations)) 259 } 260 261 func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { 262 db := dbTest.SetupDB(t) 263 ctx := context.Background() 264 265 count := params.BeaconConfig().SlotsPerEpoch * 4 266 atts := make([]*ethpb.Attestation, 0, count) 267 for i := types.Slot(0); i < params.BeaconConfig().SlotsPerEpoch; i++ { 268 for s := types.CommitteeIndex(0); s < 4; s++ { 269 blockExample := testutil.NewBeaconBlock() 270 blockExample.Block.Slot = i 271 blockExample.Block.Body.Attestations = []*ethpb.Attestation{ 272 testutil.HydrateAttestation(ðpb.Attestation{ 273 Data: ðpb.AttestationData{ 274 CommitteeIndex: s, 275 Slot: i, 276 }, 277 AggregationBits: bitfield.Bitlist{0b11}, 278 }), 279 } 280 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 281 atts = append(atts, blockExample.Block.Body.Attestations...) 282 } 283 } 284 sort.Sort(sortableAttestations(atts)) 285 286 bs := &Server{ 287 BeaconDB: db, 288 } 289 290 tests := []struct { 291 name string 292 req *ethpb.ListAttestationsRequest 293 res *ethpb.ListAttestationsResponse 294 }{ 295 { 296 name: "1st of 3 pages", 297 req: ðpb.ListAttestationsRequest{ 298 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 299 GenesisEpoch: true, 300 }, 301 PageToken: strconv.Itoa(1), 302 PageSize: 3, 303 }, 304 res: ðpb.ListAttestationsResponse{ 305 Attestations: []*ethpb.Attestation{ 306 atts[3], 307 atts[4], 308 atts[5], 309 }, 310 NextPageToken: strconv.Itoa(2), 311 TotalSize: int32(count), 312 }, 313 }, 314 { 315 name: "10 of size 1", 316 req: ðpb.ListAttestationsRequest{ 317 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 318 GenesisEpoch: true, 319 }, 320 PageToken: strconv.Itoa(10), 321 PageSize: 1, 322 }, 323 res: ðpb.ListAttestationsResponse{ 324 Attestations: []*ethpb.Attestation{ 325 atts[10], 326 }, 327 NextPageToken: strconv.Itoa(11), 328 TotalSize: int32(count), 329 }, 330 }, 331 { 332 name: "2 of size 8", 333 req: ðpb.ListAttestationsRequest{ 334 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 335 GenesisEpoch: true, 336 }, 337 PageToken: strconv.Itoa(2), 338 PageSize: 8, 339 }, 340 res: ðpb.ListAttestationsResponse{ 341 Attestations: []*ethpb.Attestation{ 342 atts[16], 343 atts[17], 344 atts[18], 345 atts[19], 346 atts[20], 347 atts[21], 348 atts[22], 349 atts[23], 350 }, 351 NextPageToken: strconv.Itoa(3), 352 TotalSize: int32(count)}, 353 }, 354 } 355 for _, test := range tests { 356 t.Run(test.name, func(t *testing.T) { 357 res, err := bs.ListAttestations(ctx, test.req) 358 require.NoError(t, err) 359 require.DeepSSZEqual(t, res, test.res) 360 }) 361 } 362 } 363 364 func TestServer_ListAttestations_Pagination_OutOfRange(t *testing.T) { 365 db := dbTest.SetupDB(t) 366 ctx := context.Background() 367 testutil.NewBeaconBlock() 368 count := types.Slot(1) 369 atts := make([]*ethpb.Attestation, 0, count) 370 for i := types.Slot(0); i < count; i++ { 371 blockExample := testutil.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{ 372 Block: ðpb.BeaconBlock{ 373 Body: ðpb.BeaconBlockBody{ 374 Attestations: []*ethpb.Attestation{ 375 { 376 Data: ðpb.AttestationData{ 377 BeaconBlockRoot: bytesutil.PadTo([]byte("root"), 32), 378 Source: ðpb.Checkpoint{Root: make([]byte, 32)}, 379 Target: ðpb.Checkpoint{Root: make([]byte, 32)}, 380 Slot: i, 381 }, 382 AggregationBits: bitfield.Bitlist{0b11}, 383 Signature: make([]byte, 96), 384 }, 385 }, 386 }, 387 }, 388 }) 389 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 390 atts = append(atts, blockExample.Block.Body.Attestations...) 391 } 392 393 bs := &Server{ 394 BeaconDB: db, 395 } 396 397 req := ðpb.ListAttestationsRequest{ 398 QueryFilter: ðpb.ListAttestationsRequest_Epoch{ 399 Epoch: 0, 400 }, 401 PageToken: strconv.Itoa(1), 402 PageSize: 100, 403 } 404 wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(atts)) 405 _, err := bs.ListAttestations(ctx, req) 406 assert.ErrorContains(t, wanted, err) 407 } 408 409 func TestServer_ListAttestations_Pagination_ExceedsMaxPageSize(t *testing.T) { 410 ctx := context.Background() 411 bs := &Server{} 412 exceedsMax := int32(cmd.Get().MaxRPCPageSize + 1) 413 414 wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, cmd.Get().MaxRPCPageSize) 415 req := ðpb.ListAttestationsRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax} 416 _, err := bs.ListAttestations(ctx, req) 417 assert.ErrorContains(t, wanted, err) 418 } 419 420 func TestServer_ListAttestations_Pagination_DefaultPageSize(t *testing.T) { 421 db := dbTest.SetupDB(t) 422 ctx := context.Background() 423 424 count := types.Slot(params.BeaconConfig().DefaultPageSize) 425 atts := make([]*ethpb.Attestation, 0, count) 426 for i := types.Slot(0); i < count; i++ { 427 blockExample := testutil.NewBeaconBlock() 428 blockExample.Block.Body.Attestations = []*ethpb.Attestation{ 429 { 430 Data: ðpb.AttestationData{ 431 BeaconBlockRoot: bytesutil.PadTo([]byte("root"), 32), 432 Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("root"), 32)}, 433 Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("root"), 32)}, 434 Slot: i, 435 }, 436 Signature: bytesutil.PadTo([]byte("root"), 96), 437 AggregationBits: bitfield.Bitlist{0b11}, 438 }, 439 } 440 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 441 atts = append(atts, blockExample.Block.Body.Attestations...) 442 } 443 444 bs := &Server{ 445 BeaconDB: db, 446 } 447 448 req := ðpb.ListAttestationsRequest{ 449 QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{ 450 GenesisEpoch: true, 451 }, 452 } 453 res, err := bs.ListAttestations(ctx, req) 454 require.NoError(t, err) 455 456 i := 0 457 j := params.BeaconConfig().DefaultPageSize 458 assert.DeepEqual(t, atts[i:j], res.Attestations, "Incorrect attestations response") 459 } 460 461 func TestServer_mapAttestationToTargetRoot(t *testing.T) { 462 count := types.Slot(100) 463 atts := make([]*ethpb.Attestation, count) 464 targetRoot1 := bytesutil.ToBytes32([]byte("root1")) 465 targetRoot2 := bytesutil.ToBytes32([]byte("root2")) 466 467 for i := types.Slot(0); i < count; i++ { 468 var targetRoot [32]byte 469 if i%2 == 0 { 470 targetRoot = targetRoot1 471 } else { 472 targetRoot = targetRoot2 473 } 474 atts[i] = ðpb.Attestation{ 475 Data: ðpb.AttestationData{ 476 Target: ðpb.Checkpoint{ 477 Root: targetRoot[:], 478 }, 479 }, 480 AggregationBits: bitfield.Bitlist{0b11}, 481 } 482 483 } 484 mappedAtts := mapAttestationsByTargetRoot(atts) 485 wantedMapLen := 2 486 wantedMapNumberOfElements := 50 487 assert.Equal(t, wantedMapLen, len(mappedAtts), "Unexpected mapped attestations length") 488 assert.Equal(t, wantedMapNumberOfElements, len(mappedAtts[targetRoot1]), "Unexpected number of attestations per block root") 489 assert.Equal(t, wantedMapNumberOfElements, len(mappedAtts[targetRoot2]), "Unexpected number of attestations per block root") 490 } 491 492 func TestServer_ListIndexedAttestations_GenesisEpoch(t *testing.T) { 493 params.UseMainnetConfig() 494 db := dbTest.SetupDB(t) 495 helpers.ClearCache() 496 ctx := context.Background() 497 targetRoot1 := bytesutil.ToBytes32([]byte("root")) 498 targetRoot2 := bytesutil.ToBytes32([]byte("root2")) 499 500 count := params.BeaconConfig().SlotsPerEpoch 501 atts := make([]*ethpb.Attestation, 0, count) 502 atts2 := make([]*ethpb.Attestation, 0, count) 503 504 for i := types.Slot(0); i < count; i++ { 505 var targetRoot [32]byte 506 if i%2 == 0 { 507 targetRoot = targetRoot1 508 } else { 509 targetRoot = targetRoot2 510 } 511 blockExample := testutil.NewBeaconBlock() 512 blockExample.Block.Body.Attestations = []*ethpb.Attestation{ 513 { 514 Signature: make([]byte, 96), 515 Data: ðpb.AttestationData{ 516 BeaconBlockRoot: make([]byte, 32), 517 Target: ðpb.Checkpoint{ 518 Root: targetRoot[:], 519 }, 520 Source: ðpb.Checkpoint{ 521 Root: make([]byte, 32), 522 }, 523 Slot: i, 524 CommitteeIndex: 0, 525 }, 526 AggregationBits: bitfield.NewBitlist(128 / uint64(params.BeaconConfig().SlotsPerEpoch)), 527 }, 528 } 529 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 530 if i%2 == 0 { 531 atts = append(atts, blockExample.Block.Body.Attestations...) 532 } else { 533 atts2 = append(atts2, blockExample.Block.Body.Attestations...) 534 } 535 536 } 537 538 // We setup 128 validators. 539 numValidators := uint64(128) 540 state, _ := testutil.DeterministicGenesisState(t, numValidators) 541 542 // Next up we convert the test attestations to indexed form: 543 indexedAtts := make([]*ethpb.IndexedAttestation, len(atts)+len(atts2)) 544 for i := 0; i < len(atts); i++ { 545 att := atts[i] 546 committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) 547 require.NoError(t, err) 548 idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts[i], committee) 549 require.NoError(t, err, "Could not convert attestation to indexed") 550 indexedAtts[i] = idxAtt 551 } 552 for i := 0; i < len(atts2); i++ { 553 att := atts2[i] 554 committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) 555 require.NoError(t, err) 556 idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts2[i], committee) 557 require.NoError(t, err, "Could not convert attestation to indexed") 558 indexedAtts[i+len(atts)] = idxAtt 559 } 560 561 bs := &Server{ 562 BeaconDB: db, 563 GenesisTimeFetcher: &chainMock.ChainService{State: state}, 564 HeadFetcher: &chainMock.ChainService{State: state}, 565 StateGen: stategen.New(db), 566 } 567 err := db.SaveStateSummary(ctx, &pbp2p.StateSummary{ 568 Root: targetRoot1[:], 569 Slot: 1, 570 }) 571 require.NoError(t, err) 572 573 err = db.SaveStateSummary(ctx, &pbp2p.StateSummary{ 574 Root: targetRoot2[:], 575 Slot: 2, 576 }) 577 require.NoError(t, err) 578 579 require.NoError(t, db.SaveState(ctx, state, bytesutil.ToBytes32(targetRoot1[:]))) 580 require.NoError(t, state.SetSlot(state.Slot()+1)) 581 require.NoError(t, db.SaveState(ctx, state, bytesutil.ToBytes32(targetRoot2[:]))) 582 res, err := bs.ListIndexedAttestations(ctx, ðpb.ListIndexedAttestationsRequest{ 583 QueryFilter: ðpb.ListIndexedAttestationsRequest_GenesisEpoch{ 584 GenesisEpoch: true, 585 }, 586 }) 587 require.NoError(t, err) 588 assert.Equal(t, len(indexedAtts), len(res.IndexedAttestations), "Incorrect indexted attestations length") 589 sort.Slice(indexedAtts, func(i, j int) bool { 590 return indexedAtts[i].Data.Slot < indexedAtts[j].Data.Slot 591 }) 592 sort.Slice(res.IndexedAttestations, func(i, j int) bool { 593 return res.IndexedAttestations[i].Data.Slot < res.IndexedAttestations[j].Data.Slot 594 }) 595 596 assert.DeepEqual(t, indexedAtts, res.IndexedAttestations, "Incorrect list indexed attestations response") 597 } 598 599 func TestServer_ListIndexedAttestations_OldEpoch(t *testing.T) { 600 params.SetupTestConfigCleanup(t) 601 params.OverrideBeaconConfig(params.MainnetConfig()) 602 db := dbTest.SetupDB(t) 603 helpers.ClearCache() 604 ctx := context.Background() 605 606 blockRoot := bytesutil.ToBytes32([]byte("root")) 607 count := params.BeaconConfig().SlotsPerEpoch 608 atts := make([]*ethpb.Attestation, 0, count) 609 epoch := types.Epoch(50) 610 startSlot, err := helpers.StartSlot(epoch) 611 require.NoError(t, err) 612 613 for i := startSlot; i < count; i++ { 614 blockExample := ðpb.SignedBeaconBlock{ 615 Block: ðpb.BeaconBlock{ 616 Body: ðpb.BeaconBlockBody{ 617 Attestations: []*ethpb.Attestation{ 618 { 619 Data: ðpb.AttestationData{ 620 BeaconBlockRoot: blockRoot[:], 621 Slot: i, 622 CommitteeIndex: 0, 623 Target: ðpb.Checkpoint{ 624 Epoch: epoch, 625 Root: make([]byte, 32), 626 }, 627 }, 628 AggregationBits: bitfield.Bitlist{0b11}, 629 }, 630 }, 631 }, 632 }, 633 } 634 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blockExample))) 635 atts = append(atts, blockExample.Block.Body.Attestations...) 636 } 637 638 // We setup 128 validators. 639 numValidators := uint64(128) 640 state, _ := testutil.DeterministicGenesisState(t, numValidators) 641 642 randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector) 643 for i := 0; i < len(randaoMixes); i++ { 644 randaoMixes[i] = make([]byte, 32) 645 } 646 require.NoError(t, state.SetRandaoMixes(randaoMixes)) 647 require.NoError(t, state.SetSlot(startSlot)) 648 649 // Next up we convert the test attestations to indexed form: 650 indexedAtts := make([]*ethpb.IndexedAttestation, len(atts)) 651 for i := 0; i < len(atts); i++ { 652 att := atts[i] 653 committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) 654 require.NoError(t, err) 655 idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts[i], committee) 656 require.NoError(t, err, "Could not convert attestation to indexed") 657 indexedAtts[i] = idxAtt 658 } 659 660 bs := &Server{ 661 BeaconDB: db, 662 GenesisTimeFetcher: &chainMock.ChainService{ 663 Genesis: time.Now(), 664 }, 665 StateGen: stategen.New(db), 666 } 667 err = db.SaveStateSummary(ctx, &pbp2p.StateSummary{ 668 Root: blockRoot[:], 669 Slot: params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch)), 670 }) 671 require.NoError(t, err) 672 require.NoError(t, db.SaveState(ctx, state, bytesutil.ToBytes32([]byte("root")))) 673 res, err := bs.ListIndexedAttestations(ctx, ðpb.ListIndexedAttestationsRequest{ 674 QueryFilter: ðpb.ListIndexedAttestationsRequest_Epoch{ 675 Epoch: epoch, 676 }, 677 }) 678 require.NoError(t, err) 679 require.DeepEqual(t, indexedAtts, res.IndexedAttestations, "Incorrect list indexed attestations response") 680 } 681 682 func TestServer_AttestationPool_Pagination_ExceedsMaxPageSize(t *testing.T) { 683 ctx := context.Background() 684 bs := &Server{} 685 exceedsMax := int32(cmd.Get().MaxRPCPageSize + 1) 686 687 wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, cmd.Get().MaxRPCPageSize) 688 req := ðpb.AttestationPoolRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax} 689 _, err := bs.AttestationPool(ctx, req) 690 assert.ErrorContains(t, wanted, err) 691 } 692 693 func TestServer_AttestationPool_Pagination_OutOfRange(t *testing.T) { 694 ctx := context.Background() 695 bs := &Server{ 696 AttestationsPool: attestations.NewPool(), 697 } 698 699 atts := []*ethpb.Attestation{ 700 { 701 Data: ðpb.AttestationData{ 702 Slot: 1, 703 BeaconBlockRoot: bytesutil.PadTo([]byte{1}, 32), 704 Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{1}, 32)}, 705 Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{1}, 32)}, 706 }, 707 AggregationBits: bitfield.Bitlist{0b1101}, 708 Signature: bytesutil.PadTo([]byte{1}, 96), 709 }, 710 { 711 Data: ðpb.AttestationData{ 712 Slot: 2, 713 BeaconBlockRoot: bytesutil.PadTo([]byte{2}, 32), 714 Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{2}, 32)}, 715 Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{2}, 32)}, 716 }, 717 AggregationBits: bitfield.Bitlist{0b1101}, 718 Signature: bytesutil.PadTo([]byte{2}, 96), 719 }, 720 { 721 Data: ðpb.AttestationData{ 722 Slot: 3, 723 BeaconBlockRoot: bytesutil.PadTo([]byte{3}, 32), 724 Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{3}, 32)}, 725 Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{3}, 32)}, 726 }, 727 AggregationBits: bitfield.Bitlist{0b1101}, 728 Signature: bytesutil.PadTo([]byte{3}, 96), 729 }, 730 } 731 require.NoError(t, bs.AttestationsPool.SaveAggregatedAttestations(atts)) 732 733 req := ðpb.AttestationPoolRequest{ 734 PageToken: strconv.Itoa(1), 735 PageSize: 100, 736 } 737 wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(atts)) 738 _, err := bs.AttestationPool(ctx, req) 739 assert.ErrorContains(t, wanted, err) 740 } 741 742 func TestServer_AttestationPool_Pagination_DefaultPageSize(t *testing.T) { 743 ctx := context.Background() 744 bs := &Server{ 745 AttestationsPool: attestations.NewPool(), 746 } 747 748 atts := make([]*ethpb.Attestation, params.BeaconConfig().DefaultPageSize+1) 749 for i := 0; i < len(atts); i++ { 750 att := testutil.NewAttestation() 751 att.Data.Slot = types.Slot(i) 752 atts[i] = att 753 } 754 require.NoError(t, bs.AttestationsPool.SaveAggregatedAttestations(atts)) 755 756 req := ðpb.AttestationPoolRequest{} 757 res, err := bs.AttestationPool(ctx, req) 758 require.NoError(t, err) 759 assert.Equal(t, params.BeaconConfig().DefaultPageSize, len(res.Attestations), "Unexpected number of attestations") 760 assert.Equal(t, params.BeaconConfig().DefaultPageSize+1, int(res.TotalSize), "Unexpected total size") 761 } 762 763 func TestServer_AttestationPool_Pagination_CustomPageSize(t *testing.T) { 764 ctx := context.Background() 765 bs := &Server{ 766 AttestationsPool: attestations.NewPool(), 767 } 768 769 numAtts := 100 770 atts := make([]*ethpb.Attestation, numAtts) 771 for i := 0; i < len(atts); i++ { 772 att := testutil.NewAttestation() 773 att.Data.Slot = types.Slot(i) 774 atts[i] = att 775 } 776 require.NoError(t, bs.AttestationsPool.SaveAggregatedAttestations(atts)) 777 tests := []struct { 778 req *ethpb.AttestationPoolRequest 779 res *ethpb.AttestationPoolResponse 780 }{ 781 { 782 req: ðpb.AttestationPoolRequest{ 783 PageToken: strconv.Itoa(1), 784 PageSize: 3, 785 }, 786 res: ðpb.AttestationPoolResponse{ 787 NextPageToken: "2", 788 TotalSize: int32(numAtts), 789 }, 790 }, 791 { 792 req: ðpb.AttestationPoolRequest{ 793 PageToken: strconv.Itoa(3), 794 PageSize: 30, 795 }, 796 res: ðpb.AttestationPoolResponse{ 797 NextPageToken: "", 798 TotalSize: int32(numAtts), 799 }, 800 }, 801 { 802 req: ðpb.AttestationPoolRequest{ 803 PageToken: strconv.Itoa(0), 804 PageSize: int32(numAtts), 805 }, 806 res: ðpb.AttestationPoolResponse{ 807 NextPageToken: "", 808 TotalSize: int32(numAtts), 809 }, 810 }, 811 } 812 for _, tt := range tests { 813 res, err := bs.AttestationPool(ctx, tt.req) 814 require.NoError(t, err) 815 assert.Equal(t, tt.res.TotalSize, res.TotalSize, "Unexpected total size") 816 assert.Equal(t, tt.res.NextPageToken, res.NextPageToken, "Unexpected next page token") 817 } 818 } 819 820 func TestServer_StreamIndexedAttestations_ContextCanceled(t *testing.T) { 821 ctx := context.Background() 822 ctx, cancel := context.WithCancel(ctx) 823 chainService := &chainMock.ChainService{} 824 server := &Server{ 825 Ctx: ctx, 826 AttestationNotifier: chainService.OperationNotifier(), 827 GenesisTimeFetcher: &chainMock.ChainService{ 828 Genesis: time.Now(), 829 }, 830 } 831 832 exitRoutine := make(chan bool) 833 ctrl := gomock.NewController(t) 834 defer ctrl.Finish() 835 mockStream := mock.NewMockBeaconChain_StreamIndexedAttestationsServer(ctrl) 836 mockStream.EXPECT().Context().Return(ctx).AnyTimes() 837 go func(tt *testing.T) { 838 err := server.StreamIndexedAttestations(&emptypb.Empty{}, mockStream) 839 assert.ErrorContains(t, "Context canceled", err) 840 <-exitRoutine 841 }(t) 842 cancel() 843 exitRoutine <- true 844 } 845 846 func TestServer_StreamIndexedAttestations_OK(t *testing.T) { 847 params.SetupTestConfigCleanup(t) 848 params.OverrideBeaconConfig(params.MainnetConfig()) 849 db := dbTest.SetupDB(t) 850 exitRoutine := make(chan bool) 851 ctrl := gomock.NewController(t) 852 defer ctrl.Finish() 853 ctx := context.Background() 854 855 numValidators := 64 856 headState, privKeys := testutil.DeterministicGenesisState(t, uint64(numValidators)) 857 b := testutil.NewBeaconBlock() 858 require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b))) 859 gRoot, err := b.Block.HashTreeRoot() 860 require.NoError(t, err) 861 require.NoError(t, db.SaveGenesisBlockRoot(ctx, gRoot)) 862 require.NoError(t, db.SaveState(ctx, headState, gRoot)) 863 864 activeIndices, err := helpers.ActiveValidatorIndices(headState, 0) 865 require.NoError(t, err) 866 epoch := types.Epoch(0) 867 attesterSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconAttester) 868 require.NoError(t, err) 869 committees, err := computeCommittees(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch)), activeIndices, attesterSeed) 870 require.NoError(t, err) 871 872 count := params.BeaconConfig().SlotsPerEpoch 873 // We generate attestations for each validator per slot per epoch. 874 atts := make(map[[32]byte][]*ethpb.Attestation) 875 for i := types.Slot(0); i < count; i++ { 876 comms := committees[i].Committees 877 for j := 0; j < numValidators; j++ { 878 var indexInCommittee uint64 879 var committeeIndex types.CommitteeIndex 880 var committeeLength int 881 var found bool 882 for comIndex, item := range comms { 883 for n, idx := range item.ValidatorIndices { 884 if types.ValidatorIndex(j) == idx { 885 indexInCommittee = uint64(n) 886 committeeIndex = types.CommitteeIndex(comIndex) 887 committeeLength = len(item.ValidatorIndices) 888 found = true 889 break 890 } 891 } 892 } 893 if !found { 894 continue 895 } 896 attExample := ðpb.Attestation{ 897 Data: ðpb.AttestationData{ 898 BeaconBlockRoot: bytesutil.PadTo([]byte("root"), 32), 899 Slot: i, 900 Source: ðpb.Checkpoint{ 901 Epoch: 0, 902 Root: gRoot[:], 903 }, 904 Target: ðpb.Checkpoint{ 905 Epoch: 0, 906 Root: gRoot[:], 907 }, 908 }, 909 } 910 domain, err := helpers.Domain(headState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, headState.GenesisValidatorRoot()) 911 require.NoError(t, err) 912 encoded, err := helpers.ComputeSigningRoot(attExample.Data, domain) 913 require.NoError(t, err) 914 sig := privKeys[j].Sign(encoded[:]) 915 attExample.Signature = sig.Marshal() 916 attExample.Data.CommitteeIndex = committeeIndex 917 aggregationBitfield := bitfield.NewBitlist(uint64(committeeLength)) 918 aggregationBitfield.SetBitAt(indexInCommittee, true) 919 attExample.AggregationBits = aggregationBitfield 920 atts[encoded] = append(atts[encoded], attExample) 921 } 922 } 923 924 chainService := &chainMock.ChainService{} 925 server := &Server{ 926 BeaconDB: db, 927 Ctx: context.Background(), 928 HeadFetcher: &chainMock.ChainService{ 929 State: headState, 930 }, 931 GenesisTimeFetcher: &chainMock.ChainService{ 932 Genesis: time.Now(), 933 }, 934 AttestationNotifier: chainService.OperationNotifier(), 935 CollectedAttestationsBuffer: make(chan []*ethpb.Attestation, 1), 936 StateGen: stategen.New(db), 937 } 938 939 for dataRoot, sameDataAtts := range atts { 940 aggAtts, err := attaggregation.Aggregate(sameDataAtts) 941 require.NoError(t, err) 942 atts[dataRoot] = aggAtts 943 } 944 945 // Next up we convert the test attestations to indexed form. 946 attsByTarget := make(map[[32]byte][]*ethpb.Attestation) 947 for _, dataRootAtts := range atts { 948 targetRoot := bytesutil.ToBytes32(dataRootAtts[0].Data.Target.Root) 949 attsByTarget[targetRoot] = append(attsByTarget[targetRoot], dataRootAtts...) 950 } 951 952 allAtts := make([]*ethpb.Attestation, 0) 953 indexedAtts := make(map[[32]byte][]*ethpb.IndexedAttestation) 954 for dataRoot, aggAtts := range attsByTarget { 955 allAtts = append(allAtts, aggAtts...) 956 for _, att := range aggAtts { 957 committee := committees[att.Data.Slot].Committees[att.Data.CommitteeIndex] 958 idxAtt, err := attestationutil.ConvertToIndexed(ctx, att, committee.ValidatorIndices) 959 require.NoError(t, err) 960 indexedAtts[dataRoot] = append(indexedAtts[dataRoot], idxAtt) 961 } 962 } 963 964 attsSent := 0 965 mockStream := mock.NewMockBeaconChain_StreamIndexedAttestationsServer(ctrl) 966 for _, atts := range indexedAtts { 967 for _, att := range atts { 968 if attsSent == len(allAtts)-1 { 969 mockStream.EXPECT().Send(att).Do(func(arg0 interface{}) { 970 exitRoutine <- true 971 }) 972 t.Log("cancelled") 973 } else { 974 mockStream.EXPECT().Send(att) 975 attsSent++ 976 } 977 } 978 } 979 mockStream.EXPECT().Context().Return(ctx).AnyTimes() 980 981 go func(tt *testing.T) { 982 assert.NoError(tt, server.StreamIndexedAttestations(&emptypb.Empty{}, mockStream), "Could not call RPC method") 983 }(t) 984 985 server.CollectedAttestationsBuffer <- allAtts 986 <-exitRoutine 987 } 988 989 func TestServer_StreamAttestations_ContextCanceled(t *testing.T) { 990 ctx := context.Background() 991 992 ctx, cancel := context.WithCancel(ctx) 993 chainService := &chainMock.ChainService{} 994 server := &Server{ 995 Ctx: ctx, 996 AttestationNotifier: chainService.OperationNotifier(), 997 } 998 999 exitRoutine := make(chan bool) 1000 ctrl := gomock.NewController(t) 1001 defer ctrl.Finish() 1002 mockStream := mock.NewMockBeaconChain_StreamAttestationsServer(ctrl) 1003 mockStream.EXPECT().Context().Return(ctx) 1004 go func(tt *testing.T) { 1005 err := server.StreamAttestations( 1006 &emptypb.Empty{}, 1007 mockStream, 1008 ) 1009 assert.ErrorContains(tt, "Context canceled", err) 1010 <-exitRoutine 1011 }(t) 1012 cancel() 1013 exitRoutine <- true 1014 } 1015 1016 func TestServer_StreamAttestations_OnSlotTick(t *testing.T) { 1017 exitRoutine := make(chan bool) 1018 ctrl := gomock.NewController(t) 1019 defer ctrl.Finish() 1020 ctx := context.Background() 1021 chainService := &chainMock.ChainService{} 1022 server := &Server{ 1023 Ctx: ctx, 1024 AttestationNotifier: chainService.OperationNotifier(), 1025 } 1026 1027 atts := []*ethpb.Attestation{ 1028 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}}), 1029 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}}), 1030 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}}), 1031 } 1032 1033 mockStream := mock.NewMockBeaconChain_StreamAttestationsServer(ctrl) 1034 mockStream.EXPECT().Send(atts[0]) 1035 mockStream.EXPECT().Send(atts[1]) 1036 mockStream.EXPECT().Send(atts[2]).Do(func(arg0 interface{}) { 1037 exitRoutine <- true 1038 }) 1039 mockStream.EXPECT().Context().Return(ctx).AnyTimes() 1040 1041 go func(tt *testing.T) { 1042 assert.NoError(tt, server.StreamAttestations(&emptypb.Empty{}, mockStream), "Could not call RPC method") 1043 }(t) 1044 for i := 0; i < len(atts); i++ { 1045 // Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed). 1046 for sent := 0; sent == 0; { 1047 sent = server.AttestationNotifier.OperationFeed().Send(&feed.Event{ 1048 Type: operation.UnaggregatedAttReceived, 1049 Data: &operation.UnAggregatedAttReceivedData{Attestation: atts[i]}, 1050 }) 1051 } 1052 } 1053 <-exitRoutine 1054 }