From 90cbe49496011f3a35a36cf79682f146bf2ca311 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 3 Dec 2019 16:33:34 -0800 Subject: [PATCH] Implement GetValidator RPC Endpoint (#4188) * include new patch targeting latest ethapis master * ensure project builds * Merge branch 'master' into update-all-api * fix up committees * Merge branch 'update-all-api' of github.com:prysmaticlabs/prysm into update-all-api * include latest eth apis * Merge branch 'master' into update-all-api * update block tests * Merge branch 'update-all-api' of github.com:prysmaticlabs/prysm into update-all-api * Merge branch 'master' into update-all-api * add todos * implement get validator rpc * add test for get validator * table driven test * fix up test * fix confs * tests for more cases * fix up tests and add out of range --- beacon-chain/rpc/beacon/validators.go | 41 +++++++++- beacon-chain/rpc/beacon/validators_test.go | 91 ++++++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/beacon-chain/rpc/beacon/validators.go b/beacon-chain/rpc/beacon/validators.go index eb4c1ba96b..b73a9c981d 100644 --- a/beacon-chain/rpc/beacon/validators.go +++ b/beacon-chain/rpc/beacon/validators.go @@ -1,6 +1,7 @@ package beacon import ( + "bytes" "context" "sort" "strconv" @@ -248,11 +249,45 @@ func (bs *Server) ListValidators( } // GetValidator information from any validator in the registry by index or public key. -// TODO(#4177): Implement. func (bs *Server) GetValidator( - _ context.Context, _ *ethpb.GetValidatorRequest, + ctx context.Context, req *ethpb.GetValidatorRequest, ) (*ethpb.Validator, error) { - return nil, status.Error(codes.Unimplemented, "Not yet implemented") + var requestingIndex bool + var index uint64 + var pubKey []byte + switch q := req.QueryFilter.(type) { + case *ethpb.GetValidatorRequest_Index: + index = q.Index + requestingIndex = true + case *ethpb.GetValidatorRequest_PublicKey: + pubKey = q.PublicKey + default: + return nil, status.Error( + codes.InvalidArgument, + "Need to specify either validator index or public key in request", + ) + } + headState, err := bs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get head state") + } + if requestingIndex { + if index >= uint64(len(headState.Validators)) { + return nil, status.Errorf( + codes.OutOfRange, + "Requesting index %d, but there are only %d validators", + index, + len(headState.Validators), + ) + } + return headState.Validators[index], nil + } + for i := 0; i < len(headState.Validators); i++ { + if bytes.Equal(headState.Validators[i].PublicKey, pubKey) { + return headState.Validators[i], nil + } + } + return nil, status.Error(codes.NotFound, "No validator matched filter criteria") } // GetValidatorActiveSetChanges retrieves the active set changes for a given epoch. diff --git a/beacon-chain/rpc/beacon/validators_test.go b/beacon-chain/rpc/beacon/validators_test.go index cfe00d0211..6691d7fa25 100644 --- a/beacon-chain/rpc/beacon/validators_test.go +++ b/beacon-chain/rpc/beacon/validators_test.go @@ -806,6 +806,97 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) { } } +func TestServer_GetValidator(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + + count := 30 + validators := make([]*ethpb.Validator, count) + for i := 0; i < count; i++ { + validators[i] = ðpb.Validator{ + ActivationEpoch: uint64(i), + PublicKey: []byte(strconv.Itoa(i)), + } + } + + bs := &Server{ + HeadFetcher: &mock.ChainService{ + State: &pbp2p.BeaconState{ + Validators: validators, + }, + }, + } + + tests := []struct { + req *ethpb.GetValidatorRequest + res *ethpb.Validator + wantErr bool + err string + }{ + { + req: ðpb.GetValidatorRequest{ + QueryFilter: ðpb.GetValidatorRequest_Index{ + Index: 0, + }, + }, + res: validators[0], + wantErr: false, + }, + { + req: ðpb.GetValidatorRequest{ + QueryFilter: ðpb.GetValidatorRequest_Index{ + Index: uint64(count - 1), + }, + }, + res: validators[count-1], + wantErr: false, + }, + { + req: ðpb.GetValidatorRequest{ + QueryFilter: ðpb.GetValidatorRequest_PublicKey{ + PublicKey: []byte(strconv.Itoa(5)), + }, + }, + res: validators[5], + wantErr: false, + }, + { + req: ðpb.GetValidatorRequest{ + QueryFilter: ðpb.GetValidatorRequest_PublicKey{ + PublicKey: []byte("bad-key"), + }, + }, + res: nil, + wantErr: true, + err: "No validator matched filter criteria", + }, + { + req: ðpb.GetValidatorRequest{ + QueryFilter: ðpb.GetValidatorRequest_Index{ + Index: uint64(len(validators)), + }, + }, + res: nil, + wantErr: true, + err: fmt.Sprintf("there are only %d validators", len(validators)), + }, + } + + for _, test := range tests { + res, err := bs.GetValidator(context.Background(), test.req) + if test.wantErr && err != nil { + if !strings.Contains(err.Error(), test.err) { + t.Fatalf("Wanted %v, received %v", test.err, err) + } + } else if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(test.res, res) { + t.Errorf("Wanted %v, got %v", test.res, res) + } + } +} + func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db)