package beacon import ( "context" "fmt" "strconv" "strings" "testing" "github.com/gogo/protobuf/proto" "github.com/prysmaticlabs/go-ssz" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/params" ) func TestServer_ListBlocks_NoResults(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) ctx := context.Background() bs := &Server{ BeaconDB: db, } wanted := ðpb.ListBlocksResponse{ BlockContainers: make([]*ethpb.BeaconBlockContainer, 0), TotalSize: int32(0), NextPageToken: strconv.Itoa(0), } res, err := bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ QueryFilter: ðpb.ListBlocksRequest_Epoch{ Epoch: 0, }, }) if err != nil { t.Fatal(err) } if !proto.Equal(wanted, res) { t.Errorf("Wanted %v, received %v", wanted, res) } res, err = bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ QueryFilter: ðpb.ListBlocksRequest_Slot{ Slot: 0, }, }) if err != nil { t.Fatal(err) } if !proto.Equal(wanted, res) { t.Errorf("Wanted %v, received %v", wanted, res) } res, err = bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ QueryFilter: ðpb.ListBlocksRequest_Root{ Root: make([]byte, 32), }, }) if err != nil { t.Fatal(err) } if !proto.Equal(wanted, res) { t.Errorf("Wanted %v, received %v", wanted, res) } } func TestServer_ListBlocks_Pagination(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) ctx := context.Background() count := uint64(100) blks := make([]*ethpb.BeaconBlock, count) blkContainers := make([]*ethpb.BeaconBlockContainer, count) for i := uint64(0); i < count; i++ { b := ðpb.BeaconBlock{ Slot: i, } root, err := ssz.SigningRoot(b) if err != nil { t.Fatal(err) } blks[i] = b blkContainers[i] = ðpb.BeaconBlockContainer{Block: b, BlockRoot: root[:]} } if err := db.SaveBlocks(ctx, blks); err != nil { t.Fatal(err) } root6, err := ssz.SigningRoot(ðpb.BeaconBlock{Slot: 6}) if err != nil { t.Fatal(err) } bs := &Server{ BeaconDB: db, } tests := []struct { req *ethpb.ListBlocksRequest res *ethpb.ListBlocksResponse }{ {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(0), QueryFilter: ðpb.ListBlocksRequest_Slot{Slot: 5}, PageSize: 3}, res: ðpb.ListBlocksResponse{ BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 5}, BlockRoot: blkContainers[5].BlockRoot}}, NextPageToken: strconv.Itoa(1), TotalSize: 1}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(0), QueryFilter: ðpb.ListBlocksRequest_Root{Root: root6[:]}, PageSize: 3}, res: ðpb.ListBlocksResponse{ BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 6}, BlockRoot: blkContainers[6].BlockRoot}}, TotalSize: 1}}, {req: ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Root{Root: root6[:]}}, res: ðpb.ListBlocksResponse{ BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 6}, BlockRoot: blkContainers[6].BlockRoot}}, TotalSize: 1}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(0), QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: 0}, PageSize: 100}, res: ðpb.ListBlocksResponse{ BlockContainers: blkContainers[0:params.BeaconConfig().SlotsPerEpoch], NextPageToken: strconv.Itoa(1), TotalSize: int32(params.BeaconConfig().SlotsPerEpoch)}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(1), QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: 5}, PageSize: 3}, res: ðpb.ListBlocksResponse{ BlockContainers: blkContainers[43:46], NextPageToken: strconv.Itoa(2), TotalSize: int32(params.BeaconConfig().SlotsPerEpoch)}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(1), QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: 11}, PageSize: 7}, res: ðpb.ListBlocksResponse{ BlockContainers: blkContainers[95:96], NextPageToken: strconv.Itoa(2), TotalSize: int32(params.BeaconConfig().SlotsPerEpoch)}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(0), QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: 12}, PageSize: 4}, res: ðpb.ListBlocksResponse{ BlockContainers: blkContainers[96:100], NextPageToken: strconv.Itoa(1), TotalSize: int32(params.BeaconConfig().SlotsPerEpoch / 2)}}, } for _, test := range tests { res, err := bs.ListBlocks(ctx, test.req) if err != nil { t.Fatal(err) } if !proto.Equal(res, test.res) { t.Errorf("Incorrect blocks response, wanted %d, received %d", len(test.res.BlockContainers), len(res.BlockContainers)) } } } func TestServer_ListBlocks_Errors(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) ctx := context.Background() bs := &Server{BeaconDB: db} exceedsMax := int32(params.BeaconConfig().MaxPageSize + 1) wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, params.BeaconConfig().MaxPageSize) req := ðpb.ListBlocksRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax} if _, err := bs.ListBlocks(ctx, req); !strings.Contains(err.Error(), wanted) { t.Errorf("Expected error %v, received %v", wanted, err) } wanted = "Must specify a filter criteria for fetching" req = ðpb.ListBlocksRequest{} if _, err := bs.ListBlocks(ctx, req); !strings.Contains(err.Error(), wanted) { t.Errorf("Expected error %v, received %v", wanted, err) } req = ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{}} res, err := bs.ListBlocks(ctx, req) if err != nil { t.Fatal(err) } if len(res.BlockContainers) != 0 { t.Errorf("wanted empty list, got a list of %d", len(res.BlockContainers)) } if res.TotalSize != 0 { t.Errorf("wanted total size 0, got size %d", res.TotalSize) } req = ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Slot{}} res, err = bs.ListBlocks(ctx, req) if err != nil { t.Fatal(err) } if len(res.BlockContainers) != 0 { t.Errorf("wanted empty list, got a list of %d", len(res.BlockContainers)) } if res.TotalSize != 0 { t.Errorf("wanted total size 0, got size %d", res.TotalSize) } req = ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Root{Root: []byte{'A'}}} res, err = bs.ListBlocks(ctx, req) if err != nil { t.Fatal(err) } if len(res.BlockContainers) != 0 { t.Errorf("wanted empty list, got a list of %d", len(res.BlockContainers)) } if res.TotalSize != 0 { t.Errorf("wanted total size 0, got size %d", res.TotalSize) } req = ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Root{Root: []byte{'A'}}} res, err = bs.ListBlocks(ctx, req) if err != nil { t.Fatal(err) } if len(res.BlockContainers) != 0 { t.Errorf("wanted empty list, got a list of %d", len(res.BlockContainers)) } if res.TotalSize != 0 { t.Errorf("wanted total size 0, got size %d", res.TotalSize) } }