mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-28 14:17:17 +00:00
5afee4ba3a
* Change streamindexed to slot ticker with offset * Make 2/3rds * Add test * Merge branch 'master' of github.com:prysmaticlabs/prysm into ticker-offset * Add check for offset * Merge branch 'master' of github.com:prysmaticlabs/prysm into ticker-offset * Fix test * Merge refs/heads/master into ticker-offset
1238 lines
34 KiB
Go
1238 lines
34 KiB
Go
package beacon
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
ptypes "github.com/gogo/protobuf/types"
|
|
"github.com/golang/mock/gomock"
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
|
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
|
"github.com/prysmaticlabs/prysm/shared/mock"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func TestServer_ListAttestations_NoResults(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{
|
|
Slot: 0,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
HeadFetcher: &chainMock.ChainService{
|
|
State: st,
|
|
},
|
|
}
|
|
wanted := ðpb.ListAttestationsResponse{
|
|
Attestations: make([]*ethpb.Attestation, 0),
|
|
TotalSize: int32(0),
|
|
NextPageToken: strconv.Itoa(0),
|
|
}
|
|
res, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{GenesisEpoch: true},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !proto.Equal(wanted, res) {
|
|
t.Errorf("Wanted %v, received %v", wanted, res)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_Genesis(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{
|
|
Slot: 0,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
HeadFetcher: &chainMock.ChainService{
|
|
State: st,
|
|
},
|
|
}
|
|
|
|
// Should throw an error if no genesis data is found.
|
|
if _, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
}); err != nil && !strings.Contains(err.Error(), "Could not find genesis") {
|
|
t.Fatal(err)
|
|
}
|
|
att := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2, CommitteeIndex: 1}}
|
|
|
|
parentRoot := [32]byte{1, 2, 3}
|
|
blk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{
|
|
Slot: 0,
|
|
ParentRoot: parentRoot[:],
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{att},
|
|
},
|
|
},
|
|
}
|
|
root, err := stateutil.BlockRoot(blk.Block)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveBlock(ctx, blk); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveGenesisBlockRoot(ctx, root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wanted := ðpb.ListAttestationsResponse{
|
|
Attestations: []*ethpb.Attestation{att},
|
|
NextPageToken: "",
|
|
TotalSize: 1,
|
|
}
|
|
|
|
res, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !proto.Equal(wanted, res) {
|
|
t.Errorf("Wanted %v, received %v", wanted, res)
|
|
}
|
|
|
|
// Should throw an error if there is more than 1 block
|
|
// for the genesis slot.
|
|
if err := db.SaveBlock(ctx, blk); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
}); err != nil && !strings.Contains(err.Error(), "Found more than 1") {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_NoPagination(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
count := uint64(8)
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
for i := uint64(0); i < count; i++ {
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Slot: i,
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: []byte("root"),
|
|
Slot: i,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
}
|
|
|
|
received, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(atts, received.Attestations) {
|
|
t.Fatalf("incorrect attestations response: wanted \n%v, received \n%v", atts, received.Attestations)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_FiltersCorrectly(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
someRoot := []byte{1, 2, 3}
|
|
sourceRoot := []byte{4, 5, 6}
|
|
sourceEpoch := uint64(5)
|
|
targetRoot := []byte{7, 8, 9}
|
|
targetEpoch := uint64(7)
|
|
|
|
blocks := []*ethpb.SignedBeaconBlock{
|
|
{
|
|
Block: ðpb.BeaconBlock{
|
|
Slot: 4,
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: someRoot,
|
|
Source: ðpb.Checkpoint{
|
|
Root: sourceRoot,
|
|
Epoch: sourceEpoch,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Root: targetRoot,
|
|
Epoch: targetEpoch,
|
|
},
|
|
Slot: 3,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Block: ðpb.BeaconBlock{
|
|
Slot: 5 + params.BeaconConfig().SlotsPerEpoch,
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: someRoot,
|
|
Source: ðpb.Checkpoint{
|
|
Root: sourceRoot,
|
|
Epoch: sourceEpoch,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Root: targetRoot,
|
|
Epoch: targetEpoch,
|
|
},
|
|
Slot: 4 + params.BeaconConfig().SlotsPerEpoch,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Block: ðpb.BeaconBlock{
|
|
Slot: 5,
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: someRoot,
|
|
Source: ðpb.Checkpoint{
|
|
Root: sourceRoot,
|
|
Epoch: sourceEpoch,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Root: targetRoot,
|
|
Epoch: targetEpoch,
|
|
},
|
|
Slot: 4,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := db.SaveBlocks(ctx, blocks); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
}
|
|
|
|
received, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_Epoch{Epoch: 1},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(received.Attestations) != 1 {
|
|
t.Errorf("Wanted 1 matching attestations for epoch %d, received %d", 1, len(received.Attestations))
|
|
}
|
|
received, err = bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{GenesisEpoch: true},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(received.Attestations) != 2 {
|
|
t.Errorf("Wanted 2 matching attestations for epoch %d, received %d", 0, len(received.Attestations))
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
count := params.BeaconConfig().SlotsPerEpoch * 4
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
|
|
for s := uint64(0); s < 4; s++ {
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Slot: i,
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
CommitteeIndex: s,
|
|
Slot: i,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
}
|
|
}
|
|
sort.Sort(sortableAttestations(atts))
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
req *ethpb.ListAttestationsRequest
|
|
res *ethpb.ListAttestationsResponse
|
|
}{
|
|
{
|
|
name: "1st of 3 pages",
|
|
req: ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
PageToken: strconv.Itoa(1),
|
|
PageSize: 3,
|
|
},
|
|
res: ðpb.ListAttestationsResponse{
|
|
Attestations: []*ethpb.Attestation{
|
|
atts[3],
|
|
atts[4],
|
|
atts[5],
|
|
},
|
|
NextPageToken: strconv.Itoa(2),
|
|
TotalSize: int32(count),
|
|
},
|
|
},
|
|
{
|
|
name: "10 of size 1",
|
|
req: ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
PageToken: strconv.Itoa(10),
|
|
PageSize: 1,
|
|
},
|
|
res: ðpb.ListAttestationsResponse{
|
|
Attestations: []*ethpb.Attestation{
|
|
atts[10],
|
|
},
|
|
NextPageToken: strconv.Itoa(11),
|
|
TotalSize: int32(count),
|
|
},
|
|
},
|
|
{
|
|
name: "2 of size 8",
|
|
req: ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
PageToken: strconv.Itoa(2),
|
|
PageSize: 8,
|
|
},
|
|
res: ðpb.ListAttestationsResponse{
|
|
Attestations: []*ethpb.Attestation{
|
|
atts[16],
|
|
atts[17],
|
|
atts[18],
|
|
atts[19],
|
|
atts[20],
|
|
atts[21],
|
|
atts[22],
|
|
atts[23],
|
|
},
|
|
NextPageToken: strconv.Itoa(3),
|
|
TotalSize: int32(count)},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
res, err := bs.ListAttestations(ctx, test.req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !proto.Equal(res, test.res) {
|
|
t.Errorf("Incorrect attestations response, wanted \n%v, received \n%v", test.res, res)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_Pagination_OutOfRange(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
count := uint64(1)
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
for i := uint64(0); i < count; i++ {
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: []byte("root"),
|
|
Slot: i,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
}
|
|
|
|
req := ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_Epoch{
|
|
Epoch: 0,
|
|
},
|
|
PageToken: strconv.Itoa(1),
|
|
PageSize: 100,
|
|
}
|
|
wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(atts))
|
|
if _, err := bs.ListAttestations(ctx, req); !strings.Contains(err.Error(), wanted) {
|
|
t.Errorf("Expected error %v, received %v", wanted, err)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_Pagination_ExceedsMaxPageSize(t *testing.T) {
|
|
ctx := context.Background()
|
|
bs := &Server{}
|
|
exceedsMax := int32(flags.Get().MaxPageSize + 1)
|
|
|
|
wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, flags.Get().MaxPageSize)
|
|
req := ðpb.ListAttestationsRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax}
|
|
if _, err := bs.ListAttestations(ctx, req); !strings.Contains(err.Error(), wanted) {
|
|
t.Errorf("Expected error %v, received %v", wanted, err)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListAttestations_Pagination_DefaultPageSize(t *testing.T) {
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
count := uint64(params.BeaconConfig().DefaultPageSize)
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
for i := uint64(0); i < count; i++ {
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: []byte("root"),
|
|
Slot: i,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
}
|
|
|
|
req := ðpb.ListAttestationsRequest{
|
|
QueryFilter: ðpb.ListAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
}
|
|
res, err := bs.ListAttestations(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
i := 0
|
|
j := params.BeaconConfig().DefaultPageSize
|
|
if !reflect.DeepEqual(res.Attestations, atts[i:j]) {
|
|
t.Log(res.Attestations, atts[i:j])
|
|
t.Error("Incorrect attestations response")
|
|
}
|
|
}
|
|
|
|
func TestServer_mapAttestationToTargetRoot(t *testing.T) {
|
|
count := uint64(100)
|
|
atts := make([]*ethpb.Attestation, count, count)
|
|
targetRoot1 := bytesutil.ToBytes32([]byte("root1"))
|
|
targetRoot2 := bytesutil.ToBytes32([]byte("root2"))
|
|
|
|
for i := uint64(0); i < count; i++ {
|
|
var targetRoot [32]byte
|
|
if i%2 == 0 {
|
|
targetRoot = targetRoot1
|
|
} else {
|
|
targetRoot = targetRoot2
|
|
}
|
|
atts[i] = ðpb.Attestation{
|
|
Data: ðpb.AttestationData{
|
|
Target: ðpb.Checkpoint{
|
|
Root: targetRoot[:],
|
|
},
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
}
|
|
|
|
}
|
|
mappedAtts := mapAttestationsByTargetRoot(atts)
|
|
wantedMapLen := 2
|
|
wantedMapNumberOfElements := 50
|
|
if len(mappedAtts) != wantedMapLen {
|
|
t.Errorf("Expected maped attestation to be of length: %d got: %d", wantedMapLen, len(mappedAtts))
|
|
}
|
|
if len(mappedAtts[targetRoot1]) != wantedMapNumberOfElements {
|
|
t.Errorf("Expected number of attestations per block root to be: %d got: %d", wantedMapNumberOfElements, len(mappedAtts[targetRoot1]))
|
|
}
|
|
if len(mappedAtts[targetRoot2]) != wantedMapNumberOfElements {
|
|
t.Errorf("Expected maped attestation to be of length: %d got: %d", wantedMapNumberOfElements, len(mappedAtts[targetRoot2]))
|
|
}
|
|
}
|
|
|
|
func TestServer_ListIndexedAttestations_NewStateManagnmentDisabled(t *testing.T) {
|
|
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{NewStateMgmt: false})
|
|
defer resetCfg()
|
|
params.SetupTestConfigCleanup(t)
|
|
params.OverrideBeaconConfig(params.MainnetConfig())
|
|
|
|
db := dbTest.SetupDB(t)
|
|
ctx := context.Background()
|
|
numValidators := uint64(128)
|
|
state, _ := testutil.DeterministicGenesisState(t, numValidators)
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
GenesisTimeFetcher: &chainMock.ChainService{State: state},
|
|
StateGen: stategen.New(db, cache.NewStateSummaryCache()),
|
|
}
|
|
_, err := bs.ListIndexedAttestations(ctx, ðpb.ListIndexedAttestationsRequest{
|
|
QueryFilter: ðpb.ListIndexedAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
})
|
|
|
|
if err == nil {
|
|
t.Error("Expecting error if new state management is off")
|
|
}
|
|
expectedErrString := "New state management must be turned on"
|
|
if e, ok := status.FromError(err); ok {
|
|
if !strings.Contains(e.Message(), expectedErrString) {
|
|
t.Errorf("expecting error message to contain %s", expectedErrString)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestServer_ListIndexedAttestations_GenesisEpoch(t *testing.T) {
|
|
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{NewStateMgmt: true})
|
|
defer resetCfg()
|
|
params.SetupTestConfigCleanup(t)
|
|
params.OverrideBeaconConfig(params.MainnetConfig())
|
|
db := dbTest.SetupDB(t)
|
|
helpers.ClearCache()
|
|
ctx := context.Background()
|
|
targetRoot1 := bytesutil.ToBytes32([]byte("root"))
|
|
targetRoot2 := bytesutil.ToBytes32([]byte("root2"))
|
|
|
|
count := params.BeaconConfig().SlotsPerEpoch
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
atts2 := make([]*ethpb.Attestation, 0, count)
|
|
|
|
for i := uint64(0); i < count; i++ {
|
|
var targetRoot [32]byte
|
|
if i%2 == 0 {
|
|
targetRoot = targetRoot1
|
|
} else {
|
|
targetRoot = targetRoot2
|
|
}
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
Target: ðpb.Checkpoint{
|
|
Root: targetRoot[:],
|
|
},
|
|
Slot: i,
|
|
CommitteeIndex: 0,
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if i%2 == 0 {
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
} else {
|
|
atts2 = append(atts2, blockExample.Block.Body.Attestations...)
|
|
}
|
|
|
|
}
|
|
|
|
// We setup 128 validators.
|
|
numValidators := uint64(128)
|
|
state, _ := testutil.DeterministicGenesisState(t, numValidators)
|
|
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
|
for i := 0; i < len(randaoMixes); i++ {
|
|
randaoMixes[i] = make([]byte, 32)
|
|
}
|
|
if err := state.SetRandaoMixes(randaoMixes); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Next up we convert the test attestations to indexed form:
|
|
indexedAtts := make([]*ethpb.IndexedAttestation, len(atts)+len(atts2), len(atts)+len(atts2))
|
|
for i := 0; i < len(atts); i++ {
|
|
att := atts[i]
|
|
committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
idxAtt := attestationutil.ConvertToIndexed(ctx, atts[i], committee)
|
|
if err != nil {
|
|
t.Fatalf("Could not convert attestation to indexed: %v", err)
|
|
}
|
|
indexedAtts[i] = idxAtt
|
|
}
|
|
for i := 0; i < len(atts2); i++ {
|
|
att := atts2[i]
|
|
committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
idxAtt := attestationutil.ConvertToIndexed(ctx, atts2[i], committee)
|
|
if err != nil {
|
|
t.Fatalf("Could not convert attestation to indexed: %v", err)
|
|
}
|
|
indexedAtts[i+len(atts)] = idxAtt
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
GenesisTimeFetcher: &chainMock.ChainService{State: state},
|
|
StateGen: stategen.New(db, cache.NewStateSummaryCache()),
|
|
}
|
|
if err := db.SaveStateSummary(ctx, &pbp2p.StateSummary{
|
|
Root: targetRoot1[:],
|
|
Slot: 1,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveStateSummary(ctx, &pbp2p.StateSummary{
|
|
Root: targetRoot2[:],
|
|
Slot: 2,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := db.SaveState(ctx, state, bytesutil.ToBytes32(targetRoot1[:])); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := state.SetSlot(state.Slot() + 1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveState(ctx, state, bytesutil.ToBytes32(targetRoot2[:])); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := bs.ListIndexedAttestations(ctx, ðpb.ListIndexedAttestationsRequest{
|
|
QueryFilter: ðpb.ListIndexedAttestationsRequest_GenesisEpoch{
|
|
GenesisEpoch: true,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res.IndexedAttestations) != len(indexedAtts) {
|
|
t.Errorf("Incorrect indexted attestations length. expected: %d got: %d", len(indexedAtts), len(res.IndexedAttestations))
|
|
}
|
|
if !reflect.DeepEqual(indexedAtts, res.IndexedAttestations) {
|
|
t.Fatalf(
|
|
"Incorrect list indexed attestations response: wanted %v, received %v",
|
|
indexedAtts,
|
|
res.IndexedAttestations,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestServer_ListIndexedAttestations_OldEpoch(t *testing.T) {
|
|
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{NewStateMgmt: true})
|
|
defer resetCfg()
|
|
params.SetupTestConfigCleanup(t)
|
|
params.OverrideBeaconConfig(params.MainnetConfig())
|
|
db := dbTest.SetupDB(t)
|
|
helpers.ClearCache()
|
|
ctx := context.Background()
|
|
|
|
blockRoot := bytesutil.ToBytes32([]byte("root"))
|
|
count := params.BeaconConfig().SlotsPerEpoch
|
|
atts := make([]*ethpb.Attestation, 0, count)
|
|
epoch := uint64(50)
|
|
startSlot := helpers.StartSlot(epoch)
|
|
|
|
for i := startSlot; i < count; i++ {
|
|
blockExample := ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Body: ðpb.BeaconBlockBody{
|
|
Attestations: []*ethpb.Attestation{
|
|
{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: blockRoot[:],
|
|
Slot: i,
|
|
CommitteeIndex: 0,
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: epoch,
|
|
Root: make([]byte, 32),
|
|
},
|
|
},
|
|
AggregationBits: bitfield.Bitlist{0b11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := db.SaveBlock(ctx, blockExample); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts = append(atts, blockExample.Block.Body.Attestations...)
|
|
}
|
|
|
|
// We setup 128 validators.
|
|
numValidators := uint64(128)
|
|
state, _ := testutil.DeterministicGenesisState(t, numValidators)
|
|
|
|
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
|
for i := 0; i < len(randaoMixes); i++ {
|
|
randaoMixes[i] = make([]byte, 32)
|
|
}
|
|
if err := state.SetRandaoMixes(randaoMixes); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := state.SetSlot(startSlot); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Next up we convert the test attestations to indexed form:
|
|
indexedAtts := make([]*ethpb.IndexedAttestation, len(atts), len(atts))
|
|
for i := 0; i < len(atts); i++ {
|
|
att := atts[i]
|
|
committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
idxAtt := attestationutil.ConvertToIndexed(ctx, atts[i], committee)
|
|
if err != nil {
|
|
t.Fatalf("Could not convert attestation to indexed: %v", err)
|
|
}
|
|
indexedAtts[i] = idxAtt
|
|
}
|
|
|
|
bs := &Server{
|
|
BeaconDB: db,
|
|
GenesisTimeFetcher: &chainMock.ChainService{
|
|
Genesis: time.Now(),
|
|
},
|
|
StateGen: stategen.New(db, cache.NewStateSummaryCache()),
|
|
}
|
|
if err := db.SaveStateSummary(ctx, &pbp2p.StateSummary{
|
|
Root: blockRoot[:],
|
|
Slot: helpers.StartSlot(epoch),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveState(ctx, state, bytesutil.ToBytes32([]byte("root"))); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := bs.ListIndexedAttestations(ctx, ðpb.ListIndexedAttestationsRequest{
|
|
QueryFilter: ðpb.ListIndexedAttestationsRequest_Epoch{
|
|
Epoch: epoch,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(indexedAtts, res.IndexedAttestations) {
|
|
t.Fatalf(
|
|
"Incorrect list indexed attestations response: wanted %v, received %v",
|
|
indexedAtts,
|
|
res.IndexedAttestations,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestServer_AttestationPool_Pagination_ExceedsMaxPageSize(t *testing.T) {
|
|
ctx := context.Background()
|
|
bs := &Server{}
|
|
exceedsMax := int32(flags.Get().MaxPageSize + 1)
|
|
|
|
wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, flags.Get().MaxPageSize)
|
|
req := ðpb.AttestationPoolRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax}
|
|
if _, err := bs.AttestationPool(ctx, req); err != nil && !strings.Contains(err.Error(), wanted) {
|
|
t.Errorf("Expected error %v, received %v", wanted, err)
|
|
}
|
|
}
|
|
|
|
func TestServer_AttestationPool_Pagination_OutOfRange(t *testing.T) {
|
|
ctx := context.Background()
|
|
bs := &Server{
|
|
AttestationsPool: attestations.NewPool(),
|
|
}
|
|
|
|
atts := []*ethpb.Attestation{
|
|
{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
}
|
|
if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req := ðpb.AttestationPoolRequest{
|
|
PageToken: strconv.Itoa(1),
|
|
PageSize: 100,
|
|
}
|
|
wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(atts))
|
|
if _, err := bs.AttestationPool(ctx, req); err != nil && !strings.Contains(err.Error(), wanted) {
|
|
t.Errorf("Expected error %v, received %v", wanted, err)
|
|
}
|
|
}
|
|
|
|
func TestServer_AttestationPool_Pagination_DefaultPageSize(t *testing.T) {
|
|
ctx := context.Background()
|
|
bs := &Server{
|
|
AttestationsPool: attestations.NewPool(),
|
|
}
|
|
|
|
atts := make([]*ethpb.Attestation, params.BeaconConfig().DefaultPageSize+1)
|
|
for i := 0; i < len(atts); i++ {
|
|
atts[i] = ðpb.Attestation{
|
|
Data: ðpb.AttestationData{Slot: uint64(i)},
|
|
AggregationBits: bitfield.Bitlist{0b1101},
|
|
}
|
|
}
|
|
if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req := ðpb.AttestationPoolRequest{}
|
|
res, err := bs.AttestationPool(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(res.Attestations) != params.BeaconConfig().DefaultPageSize {
|
|
t.Errorf(
|
|
"Wanted %d attestations in response, received %d",
|
|
params.BeaconConfig().DefaultPageSize,
|
|
len(res.Attestations),
|
|
)
|
|
}
|
|
if int(res.TotalSize) != params.BeaconConfig().DefaultPageSize+1 {
|
|
t.Errorf("Wanted total size %d, received %d", params.BeaconConfig().DefaultPageSize+1, res.TotalSize)
|
|
}
|
|
}
|
|
|
|
func TestServer_AttestationPool_Pagination_CustomPageSize(t *testing.T) {
|
|
ctx := context.Background()
|
|
bs := &Server{
|
|
AttestationsPool: attestations.NewPool(),
|
|
}
|
|
|
|
numAtts := 100
|
|
atts := make([]*ethpb.Attestation, numAtts)
|
|
for i := 0; i < len(atts); i++ {
|
|
atts[i] = ðpb.Attestation{
|
|
Data: ðpb.AttestationData{Slot: uint64(i)},
|
|
AggregationBits: bitfield.Bitlist{0b1101},
|
|
}
|
|
}
|
|
if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tests := []struct {
|
|
req *ethpb.AttestationPoolRequest
|
|
res *ethpb.AttestationPoolResponse
|
|
}{
|
|
{
|
|
req: ðpb.AttestationPoolRequest{
|
|
PageToken: strconv.Itoa(1),
|
|
PageSize: 3,
|
|
},
|
|
res: ðpb.AttestationPoolResponse{
|
|
NextPageToken: "2",
|
|
TotalSize: int32(numAtts),
|
|
},
|
|
},
|
|
{
|
|
req: ðpb.AttestationPoolRequest{
|
|
PageToken: strconv.Itoa(3),
|
|
PageSize: 30,
|
|
},
|
|
res: ðpb.AttestationPoolResponse{
|
|
NextPageToken: "",
|
|
TotalSize: int32(numAtts),
|
|
},
|
|
},
|
|
{
|
|
req: ðpb.AttestationPoolRequest{
|
|
PageToken: strconv.Itoa(0),
|
|
PageSize: int32(numAtts),
|
|
},
|
|
res: ðpb.AttestationPoolResponse{
|
|
NextPageToken: "1",
|
|
TotalSize: int32(numAtts),
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
res, err := bs.AttestationPool(ctx, tt.req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res.TotalSize != tt.res.TotalSize {
|
|
t.Errorf("Wanted total size %d, received %d", tt.res.TotalSize, res.TotalSize)
|
|
}
|
|
if res.NextPageToken != tt.res.NextPageToken {
|
|
t.Errorf("Wanted next page token %s, received %s", tt.res.NextPageToken, res.NextPageToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestServer_StreamIndexedAttestations_ContextCanceled(t *testing.T) {
|
|
ctx := context.Background()
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
chainService := &chainMock.ChainService{}
|
|
server := &Server{
|
|
Ctx: ctx,
|
|
AttestationNotifier: chainService.OperationNotifier(),
|
|
GenesisTimeFetcher: &chainMock.ChainService{
|
|
Genesis: time.Now(),
|
|
},
|
|
}
|
|
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
mockStream := mock.NewMockBeaconChain_StreamIndexedAttestationsServer(ctrl)
|
|
mockStream.EXPECT().Context().Return(ctx).AnyTimes()
|
|
go func(tt *testing.T) {
|
|
if err := server.StreamIndexedAttestations(
|
|
&ptypes.Empty{},
|
|
mockStream,
|
|
); err != nil && !strings.Contains(err.Error(), "Context canceled") {
|
|
tt.Errorf("Expected context canceled error got: %v", err)
|
|
}
|
|
<-exitRoutine
|
|
}(t)
|
|
cancel()
|
|
exitRoutine <- true
|
|
}
|
|
|
|
func TestServer_StreamIndexedAttestations_OK(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
params.OverrideBeaconConfig(params.MainnetConfig())
|
|
db := dbTest.SetupDB(t)
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
ctx := context.Background()
|
|
|
|
numValidators := 64
|
|
headState, privKeys := testutil.DeterministicGenesisState(t, uint64(numValidators))
|
|
b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}}
|
|
if err := db.SaveBlock(ctx, b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
gRoot, err := stateutil.BlockRoot(b.Block)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveGenesisBlockRoot(ctx, gRoot); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.SaveState(ctx, headState, gRoot); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
activeIndices, err := helpers.ActiveValidatorIndices(headState, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
epoch := uint64(0)
|
|
attesterSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
committees, err := computeCommittees(helpers.StartSlot(epoch), activeIndices, attesterSeed)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
count := params.BeaconConfig().SlotsPerEpoch
|
|
// We generate attestations for each validator per slot per epoch.
|
|
atts := make(map[[32]byte][]*ethpb.Attestation)
|
|
for i := uint64(0); i < count; i++ {
|
|
comms := committees[i].Committees
|
|
for j := 0; j < numValidators; j++ {
|
|
var indexInCommittee uint64
|
|
var committeeIndex uint64
|
|
var committeeLength int
|
|
var found bool
|
|
for comIndex, item := range comms {
|
|
for n, idx := range item.ValidatorIndices {
|
|
if uint64(j) == idx {
|
|
indexInCommittee = uint64(n)
|
|
committeeIndex = uint64(comIndex)
|
|
committeeLength = len(item.ValidatorIndices)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
continue
|
|
}
|
|
attExample := ðpb.Attestation{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("root"), 32),
|
|
Slot: i,
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 0,
|
|
Root: gRoot[:],
|
|
},
|
|
},
|
|
}
|
|
domain, err := helpers.Domain(headState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, headState.GenesisValidatorRoot())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
encoded, err := helpers.ComputeSigningRoot(attExample.Data, domain)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sig := privKeys[j].Sign(encoded[:])
|
|
attExample.Signature = sig.Marshal()
|
|
attExample.Data.CommitteeIndex = committeeIndex
|
|
aggregationBitfield := bitfield.NewBitlist(uint64(committeeLength))
|
|
aggregationBitfield.SetBitAt(indexInCommittee, true)
|
|
attExample.AggregationBits = aggregationBitfield
|
|
atts[encoded] = append(atts[encoded], attExample)
|
|
}
|
|
}
|
|
|
|
chainService := &chainMock.ChainService{}
|
|
server := &Server{
|
|
BeaconDB: db,
|
|
Ctx: context.Background(),
|
|
HeadFetcher: &chainMock.ChainService{
|
|
State: headState,
|
|
},
|
|
GenesisTimeFetcher: &chainMock.ChainService{
|
|
Genesis: time.Now(),
|
|
},
|
|
AttestationNotifier: chainService.OperationNotifier(),
|
|
CollectedAttestationsBuffer: make(chan []*ethpb.Attestation, 1),
|
|
StateGen: stategen.New(db, cache.NewStateSummaryCache()),
|
|
}
|
|
|
|
for dataRoot, sameDataAtts := range atts {
|
|
aggAtts, err := helpers.AggregateAttestations(sameDataAtts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
atts[dataRoot] = aggAtts
|
|
}
|
|
|
|
// Next up we convert the test attestations to indexed form.
|
|
attsByTarget := make(map[[32]byte][]*ethpb.Attestation)
|
|
for _, dataRootAtts := range atts {
|
|
targetRoot := bytesutil.ToBytes32(dataRootAtts[0].Data.Target.Root)
|
|
attsByTarget[targetRoot] = append(attsByTarget[targetRoot], dataRootAtts...)
|
|
}
|
|
|
|
allAtts := make([]*ethpb.Attestation, 0)
|
|
indexedAtts := make(map[[32]byte][]*ethpb.IndexedAttestation)
|
|
for dataRoot, aggAtts := range attsByTarget {
|
|
allAtts = append(allAtts, aggAtts...)
|
|
for _, att := range aggAtts {
|
|
committee := committees[att.Data.Slot].Committees[att.Data.CommitteeIndex]
|
|
idxAtt := attestationutil.ConvertToIndexed(ctx, att, committee.ValidatorIndices)
|
|
indexedAtts[dataRoot] = append(indexedAtts[dataRoot], idxAtt)
|
|
}
|
|
}
|
|
|
|
attsSent := 0
|
|
mockStream := mock.NewMockBeaconChain_StreamIndexedAttestationsServer(ctrl)
|
|
for _, atts := range indexedAtts {
|
|
for _, att := range atts {
|
|
if attsSent == len(allAtts)-1 {
|
|
mockStream.EXPECT().Send(att).Do(func(arg0 interface{}) {
|
|
exitRoutine <- true
|
|
})
|
|
t.Log("cancelled")
|
|
} else {
|
|
mockStream.EXPECT().Send(att)
|
|
attsSent++
|
|
}
|
|
}
|
|
}
|
|
mockStream.EXPECT().Context().Return(ctx).AnyTimes()
|
|
|
|
go func(tt *testing.T) {
|
|
if err := server.StreamIndexedAttestations(&ptypes.Empty{}, mockStream); err != nil {
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
}
|
|
}(t)
|
|
|
|
server.CollectedAttestationsBuffer <- allAtts
|
|
<-exitRoutine
|
|
}
|
|
|
|
func TestServer_StreamAttestations_ContextCanceled(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
chainService := &chainMock.ChainService{}
|
|
server := &Server{
|
|
Ctx: ctx,
|
|
AttestationNotifier: chainService.OperationNotifier(),
|
|
}
|
|
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
mockStream := mock.NewMockBeaconChain_StreamAttestationsServer(ctrl)
|
|
mockStream.EXPECT().Context().Return(ctx)
|
|
go func(tt *testing.T) {
|
|
if err := server.StreamAttestations(
|
|
&ptypes.Empty{},
|
|
mockStream,
|
|
); !strings.Contains(err.Error(), "Context canceled") {
|
|
tt.Errorf("Expected context canceled error got: %v", err)
|
|
}
|
|
<-exitRoutine
|
|
}(t)
|
|
cancel()
|
|
exitRoutine <- true
|
|
}
|
|
|
|
func TestServer_StreamAttestations_OnSlotTick(t *testing.T) {
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
ctx := context.Background()
|
|
chainService := &chainMock.ChainService{}
|
|
server := &Server{
|
|
Ctx: ctx,
|
|
AttestationNotifier: chainService.OperationNotifier(),
|
|
}
|
|
|
|
atts := []*ethpb.Attestation{
|
|
{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}},
|
|
}
|
|
|
|
mockStream := mock.NewMockBeaconChain_StreamAttestationsServer(ctrl)
|
|
mockStream.EXPECT().Send(atts[0])
|
|
mockStream.EXPECT().Send(atts[1])
|
|
mockStream.EXPECT().Send(atts[2]).Do(func(arg0 interface{}) {
|
|
exitRoutine <- true
|
|
})
|
|
mockStream.EXPECT().Context().Return(ctx).AnyTimes()
|
|
|
|
go func(tt *testing.T) {
|
|
if err := server.StreamAttestations(&ptypes.Empty{}, mockStream); err != nil {
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
}
|
|
}(t)
|
|
for i := 0; i < len(atts); i++ {
|
|
// Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed).
|
|
for sent := 0; sent == 0; {
|
|
sent = server.AttestationNotifier.OperationFeed().Send(&feed.Event{
|
|
Type: operation.UnaggregatedAttReceived,
|
|
Data: &operation.UnAggregatedAttReceivedData{Attestation: atts[i]},
|
|
})
|
|
}
|
|
}
|
|
<-exitRoutine
|
|
}
|