package rpc import ( "context" "fmt" "reflect" "strconv" "strings" "testing" "github.com/gogo/protobuf/proto" "github.com/prysmaticlabs/prysm/beacon-chain/internal" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/params" ) func TestBeaconChainServer_ListValidatorBalances(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 100 balances := make([]uint64, count) validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } balances[i] = uint64(i) validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators, Balances: balances}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } tests := []struct { req *ethpb.GetValidatorBalancesRequest res *ethpb.ValidatorBalances }{ {req: ðpb.GetValidatorBalancesRequest{PublicKeys: [][]byte{{99}}}, res: ðpb.ValidatorBalances{Balances: []*ethpb.ValidatorBalances_Balance{{ Index: 99, PublicKey: []byte{99}, Balance: 99}}, }}, {req: ðpb.GetValidatorBalancesRequest{Indices: []uint64{1, 2, 3}}, res: ðpb.ValidatorBalances{Balances: []*ethpb.ValidatorBalances_Balance{ {Index: 1, PublicKey: []byte{1}, Balance: 1}, {Index: 2, PublicKey: []byte{2}, Balance: 2}, {Index: 3, PublicKey: []byte{3}, Balance: 3}}, }}, {req: ðpb.GetValidatorBalancesRequest{PublicKeys: [][]byte{{10}, {11}, {12}}}, res: ðpb.ValidatorBalances{Balances: []*ethpb.ValidatorBalances_Balance{ {Index: 10, PublicKey: []byte{10}, Balance: 10}, {Index: 11, PublicKey: []byte{11}, Balance: 11}, {Index: 12, PublicKey: []byte{12}, Balance: 12}}, }}, {req: ðpb.GetValidatorBalancesRequest{PublicKeys: [][]byte{{2}, {3}}, Indices: []uint64{3, 4}}, // Duplication res: ðpb.ValidatorBalances{Balances: []*ethpb.ValidatorBalances_Balance{ {Index: 2, PublicKey: []byte{2}, Balance: 2}, {Index: 3, PublicKey: []byte{3}, Balance: 3}, {Index: 4, PublicKey: []byte{4}, Balance: 4}}, }}, } for _, test := range tests { res, err := bs.ListValidatorBalances(context.Background(), test.req) if err != nil { t.Fatal(err) } if !proto.Equal(res, test.res) { t.Error("Incorrect respond of validator balances") } } } func TestBeaconChainServer_ListValidatorBalancesOutOfRange(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 1 balances := make([]uint64, count) validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } balances[i] = uint64(i) validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators, Balances: balances}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } req := ðpb.GetValidatorBalancesRequest{Indices: []uint64{uint64(count)}} wanted := fmt.Sprintf("validator index %d >= balance list %d", count, len(balances)) if _, err := bs.ListValidatorBalances(context.Background(), req); !strings.Contains(err.Error(), wanted) { t.Errorf("Expected error %v, received %v", wanted, err) } } func TestBeaconChainServer_GetValidatorsNoPagination(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 100 validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } received, err := bs.GetValidators(context.Background(), ðpb.GetValidatorsRequest{}) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(validators, received.Validators) { fmt.Println(received.Validators) t.Fatal("Incorrect respond of validators") } } func TestBeaconChainServer_GetValidatorsPagination(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 100 balances := make([]uint64, count) validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } balances[i] = uint64(i) validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators, Balances: balances}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } tests := []struct { req *ethpb.GetValidatorsRequest res *ethpb.Validators }{ {req: ðpb.GetValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 3}, res: ðpb.Validators{ Validators: []*ethpb.Validator{ {PublicKey: []byte{3}}, {PublicKey: []byte{4}}, {PublicKey: []byte{5}}}, NextPageToken: strconv.Itoa(2), TotalSize: int32(count)}}, {req: ðpb.GetValidatorsRequest{PageToken: strconv.Itoa(10), PageSize: 5}, res: ðpb.Validators{ Validators: []*ethpb.Validator{ {PublicKey: []byte{50}}, {PublicKey: []byte{51}}, {PublicKey: []byte{52}}, {PublicKey: []byte{53}}, {PublicKey: []byte{54}}}, NextPageToken: strconv.Itoa(11), TotalSize: int32(count)}}, {req: ðpb.GetValidatorsRequest{PageToken: strconv.Itoa(33), PageSize: 3}, res: ðpb.Validators{ Validators: []*ethpb.Validator{ {PublicKey: []byte{99}}}, NextPageToken: strconv.Itoa(34), TotalSize: int32(count)}}, {req: ðpb.GetValidatorsRequest{PageSize: 2}, res: ðpb.Validators{ Validators: []*ethpb.Validator{ {PublicKey: []byte{0}}, {PublicKey: []byte{1}}}, NextPageToken: strconv.Itoa(1), TotalSize: int32(count)}}, } for _, test := range tests { res, err := bs.GetValidators(context.Background(), test.req) if err != nil { t.Fatal(err) } if !proto.Equal(res, test.res) { t.Error("Incorrect respond of validators") } } } func TestBeaconChainServer_GetValidatorsPaginationOutOfRange(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 1 validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } req := ðpb.GetValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 100} wanted := fmt.Sprintf("page start %d >= validator list %d", req.PageSize, len(validators)) if _, err := bs.GetValidators(context.Background(), req); !strings.Contains(err.Error(), wanted) { t.Errorf("Expected error %v, received %v", wanted, err) } } func TestBeaconChainServer_GetValidatorsMaxPageSize(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 1000 validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } start := 1 max := params.BeaconConfig().MaxPageSize exceedsMax := int32(max + 1) req := ðpb.GetValidatorsRequest{PageToken: strconv.Itoa(start), PageSize: exceedsMax} res, err := bs.GetValidators(context.Background(), req) if err != nil { t.Fatal(err) } i := start * max j := (start + 1) * max if !reflect.DeepEqual(res.Validators, validators[i:j]) { t.Error("Incorrect respond of validators") } } func TestBeaconChainServer_GetValidatorsDefaultPageSize(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) count := 1000 validators := make([]*ethpb.Validator, 0, count) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex([]byte{byte(i)}, i); err != nil { t.Fatal(err) } validators = append(validators, ðpb.Validator{PublicKey: []byte{byte(i)}}) } if err := db.SaveState( context.Background(), &pbp2p.BeaconState{Validators: validators}); err != nil { t.Fatal(err) } bs := &BeaconChainServer{ beaconDB: db, } req := ðpb.GetValidatorsRequest{} res, err := bs.GetValidators(context.Background(), req) if err != nil { t.Fatal(err) } i := 0 j := params.BeaconConfig().DefaultPageSize if !reflect.DeepEqual(res.Validators, validators[i:j]) { t.Error("Incorrect respond of validators") } }