Skip to content

Commit

Permalink
Ensure Consistent ListValidators RPC Results (#6054)
Browse files Browse the repository at this point in the history
* run process slots on list vals call
* fix behavior
* Merge branch 'master' into bad-list-vals
* Merge refs/heads/master into bad-list-vals
* Merge refs/heads/master into bad-list-vals
* Merge refs/heads/master into bad-list-vals
* Merge refs/heads/master into bad-list-vals
* Merge refs/heads/master into bad-list-vals
* Merge refs/heads/master into bad-list-vals
  • Loading branch information
rauljordan committed Jun 1, 2020
1 parent 261a343 commit c23ac85
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 9 deletions.
31 changes: 22 additions & 9 deletions beacon-chain/rpc/beacon/validators.go
Expand Up @@ -323,7 +323,7 @@ func (bs *Server) ListValidators(
if err != nil {
return nil, status.Error(codes.Internal, "Could not get head state")
}
currentEpoch := helpers.CurrentEpoch(headState)
currentEpoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot())
requestedEpoch := currentEpoch

switch q := req.QueryFilter.(type) {
Expand All @@ -332,9 +332,30 @@ func (bs *Server) ListValidators(
requestedEpoch = 0
}
case *ethpb.ListValidatorsRequest_Epoch:
if q.Epoch > currentEpoch {
return nil, status.Errorf(
codes.InvalidArgument,
"Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d",
currentEpoch,
q.Epoch,
)
}
requestedEpoch = q.Epoch
}

if helpers.StartSlot(requestedEpoch) > headState.Slot() {
headState = headState.Copy()
headState, err = state.ProcessSlots(ctx, headState, helpers.StartSlot(requestedEpoch))
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not process slots up to %d: %v",
helpers.StartSlot(requestedEpoch),
err,
)
}
}

validatorList := make([]*ethpb.Validators_ValidatorContainer, 0)

for _, index := range req.Indices {
Expand Down Expand Up @@ -396,14 +417,6 @@ func (bs *Server) ListValidators(
}
}
validatorList = validatorList[:stopIdx]
} else if requestedEpoch > currentEpoch {
// Otherwise, we are requesting data from the future and we return an error.
return nil, status.Errorf(
codes.InvalidArgument,
"Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d",
currentEpoch,
requestedEpoch,
)
}

// Filter active validators if the request specifies it.
Expand Down
124 changes: 124 additions & 0 deletions beacon-chain/rpc/beacon/validators_test.go
Expand Up @@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"testing"
"time"

"github.com/gogo/protobuf/proto"
ptypes "github.com/gogo/protobuf/types"
Expand Down Expand Up @@ -485,6 +486,10 @@ func TestServer_ListValidators_CannotRequestFutureEpoch(t *testing.T) {
}
bs := &Server{
BeaconDB: db,
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
HeadFetcher: &mock.ChainService{
State: st,
},
Expand Down Expand Up @@ -615,6 +620,10 @@ func TestServer_ListValidators_NoResults(t *testing.T) {

bs := &Server{
BeaconDB: db,
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
HeadFetcher: &mock.ChainService{
State: st,
},
Expand Down Expand Up @@ -685,6 +694,10 @@ func TestServer_ListValidators_OnlyActiveValidators(t *testing.T) {
HeadFetcher: &mock.ChainService{
State: st,
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
}

received, err := bs.ListValidators(ctx, &ethpb.ListValidatorsRequest{
Expand Down Expand Up @@ -719,6 +732,10 @@ func TestServer_ListValidators_NoPagination(t *testing.T) {
HeadFetcher: &mock.ChainService{
State: headState,
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Expand Down Expand Up @@ -768,6 +785,10 @@ func TestServer_ListValidators_IndicesPubKeys(t *testing.T) {
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
}

pubKeysWanted := make([][]byte, len(pubkeyIndicesWanted))
Expand Down Expand Up @@ -808,6 +829,10 @@ func TestServer_ListValidators_Pagination(t *testing.T) {
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
}

tests := []struct {
Expand Down Expand Up @@ -946,6 +971,10 @@ func TestServer_ListValidators_PaginationOutOfRange(t *testing.T) {
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
}

req := &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 100}
Expand Down Expand Up @@ -991,6 +1020,10 @@ func TestServer_ListValidators_DefaultPageSize(t *testing.T) {
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 0.
Genesis: time.Now(),
},
}

req := &ethpb.ListValidatorsRequest{}
Expand Down Expand Up @@ -1031,10 +1064,15 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
if err := st.SetValidators(validators); err != nil {
t.Fatal(err)
}
secondsPerEpoch := params.BeaconConfig().SecondsPerSlot * params.BeaconConfig().SlotsPerEpoch
bs := &Server{
HeadFetcher: &mock.ChainService{
State: st,
},
GenesisTimeFetcher: &mock.ChainService{
// We are in epoch 30
Genesis: time.Now().Add(time.Duration(-1*int64(30*secondsPerEpoch)) * time.Second),
},
}

req := &ethpb.ListValidatorsRequest{
Expand Down Expand Up @@ -1064,6 +1102,92 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
}
}

func TestServer_ListValidators_ProcessHeadStateSlots(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
headSlot := uint64(0)
numValidators := uint64(64)
validators := make([]*ethpb.Validator, numValidators)
balances := make([]uint64, numValidators)
for i := uint64(0); i < numValidators; i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: 0,
PublicKey: make([]byte, 48),
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
want := make([]*ethpb.Validators_ValidatorContainer, len(validators))
for i := 0; i < len(validators); i++ {
want[i] = &ethpb.Validators_ValidatorContainer{
Index: uint64(i),
Validator: validators[i],
}
}

st := testutil.NewBeaconState()
if err := st.SetSlot(headSlot); err != nil {
t.Fatal(err)
}
if err := st.SetValidators(validators); err != nil {
t.Fatal(err)
}
if err := st.SetBalances(balances); err != nil {
t.Fatal(err)
}
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
stateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(blockRoots); i++ {
blockRoots[i] = make([]byte, 32)
stateRoots[i] = make([]byte, 32)
}
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(randaoMixes); i++ {
randaoMixes[i] = make([]byte, 32)
}
slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
if err := st.SetBlockRoots(blockRoots); err != nil {
t.Fatal(err)
}
if err := st.SetStateRoots(stateRoots); err != nil {
t.Fatal(err)
}
if err := st.SetRandaoMixes(randaoMixes); err != nil {
t.Fatal(err)
}
if err := st.SetSlashings(slashings); err != nil {
t.Fatal(err)
}
secondsPerEpoch := params.BeaconConfig().SecondsPerSlot * params.BeaconConfig().SlotsPerEpoch
bs := &Server{
HeadFetcher: &mock.ChainService{
State: st,
},
GenesisTimeFetcher: &mock.ChainService{
Genesis: time.Now().Add(time.Duration(-1*int64(secondsPerEpoch)) * time.Second),
},
}

req := &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Epoch{
Epoch: 1,
},
}
res, err := bs.ListValidators(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if len(res.ValidatorList) != len(want) {
t.Errorf("Incorrect number of validators, wanted %d received %d", len(want), len(res.ValidatorList))
}
for i := 0; i < len(res.ValidatorList); i++ {
if !reflect.DeepEqual(res.ValidatorList[i], want[i]) {
t.Errorf("Wanted validator %d: %v, got %v", i, want[i], res.ValidatorList[i])
}
}
}

func TestServer_GetValidator(t *testing.T) {
count := 30
validators := make([]*ethpb.Validator, count)
Expand Down

0 comments on commit c23ac85

Please sign in to comment.