diff --git a/beacon-chain/rpc/beacon/validators.go b/beacon-chain/rpc/beacon/validators.go index d13971369..6c8c3397c 100644 --- a/beacon-chain/rpc/beacon/validators.go +++ b/beacon-chain/rpc/beacon/validators.go @@ -361,6 +361,94 @@ func (bs *Server) GetValidator( func (bs *Server) GetValidatorActiveSetChanges( ctx context.Context, req *ethpb.GetValidatorActiveSetChangesRequest, ) (*ethpb.ActiveSetChanges, error) { + + if featureconfig.Get().DisableNewStateMgmt { + return bs.getValidatorActiveSetChangesUsingOldArchival(ctx, req) + } + + currentEpoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + + var requestedEpoch uint64 + switch q := req.QueryFilter.(type) { + case *ethpb.GetValidatorActiveSetChangesRequest_Genesis: + requestedEpoch = 0 + case *ethpb.GetValidatorActiveSetChangesRequest_Epoch: + requestedEpoch = q.Epoch + default: + requestedEpoch = currentEpoch + } + if requestedEpoch > currentEpoch { + return nil, status.Errorf( + codes.InvalidArgument, + "Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d", + currentEpoch, + requestedEpoch, + ) + } + + activatedIndices := make([]uint64, 0) + exitedIndices := make([]uint64, 0) + slashedIndices := make([]uint64, 0) + ejectedIndices := make([]uint64, 0) + + requestedState, err := bs.StateGen.StateBySlot(ctx, helpers.StartSlot(requestedEpoch)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get state: %v", err) + } + + activeValidatorCount, err := helpers.ActiveValidatorCount(requestedState, helpers.CurrentEpoch(requestedState)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get active validator count: %v", err) + } + vs := requestedState.Validators() + activatedIndices = validators.ActivatedValidatorIndices(helpers.CurrentEpoch(requestedState), vs) + exitedIndices, err = validators.ExitedValidatorIndices(helpers.CurrentEpoch(requestedState), vs, activeValidatorCount) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not determine exited validator indices: %v", err) + } + slashedIndices = validators.SlashedValidatorIndices(helpers.CurrentEpoch(requestedState), vs) + ejectedIndices, err = validators.EjectedValidatorIndices(helpers.CurrentEpoch(requestedState), vs, activeValidatorCount) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not determine ejected validator indices: %v", err) + } + + // Retrieve public keys for the indices. + activatedKeys := make([][]byte, len(activatedIndices)) + exitedKeys := make([][]byte, len(exitedIndices)) + slashedKeys := make([][]byte, len(slashedIndices)) + ejectedKeys := make([][]byte, len(ejectedIndices)) + for i, idx := range activatedIndices { + pubkey := requestedState.PubkeyAtIndex(idx) + activatedKeys[i] = pubkey[:] + } + for i, idx := range exitedIndices { + pubkey := requestedState.PubkeyAtIndex(idx) + exitedKeys[i] = pubkey[:] + } + for i, idx := range slashedIndices { + pubkey := requestedState.PubkeyAtIndex(idx) + slashedKeys[i] = pubkey[:] + } + for i, idx := range ejectedIndices { + pubkey := requestedState.PubkeyAtIndex(idx) + ejectedKeys[i] = pubkey[:] + } + return ðpb.ActiveSetChanges{ + Epoch: requestedEpoch, + ActivatedPublicKeys: activatedKeys, + ActivatedIndices: activatedIndices, + ExitedPublicKeys: exitedKeys, + ExitedIndices: exitedIndices, + SlashedPublicKeys: slashedKeys, + SlashedIndices: slashedIndices, + EjectedPublicKeys: ejectedKeys, + EjectedIndices: ejectedIndices, + }, nil +} + +func (bs *Server) getValidatorActiveSetChangesUsingOldArchival( + ctx context.Context, req *ethpb.GetValidatorActiveSetChangesRequest, +) (*ethpb.ActiveSetChanges, error) { headState, err := bs.HeadFetcher.HeadState(ctx) if err != nil { return nil, status.Error(codes.Internal, "Could not get head state") diff --git a/beacon-chain/rpc/beacon/validators_test.go b/beacon-chain/rpc/beacon/validators_test.go index 78ecc2d11..2fb99a1cf 100644 --- a/beacon-chain/rpc/beacon/validators_test.go +++ b/beacon-chain/rpc/beacon/validators_test.go @@ -36,6 +36,27 @@ func init() { }) } +func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing.T) { + ctx := context.Background() + st := testutil.NewBeaconState() + if err := st.SetSlot(0); err != nil { + t.Fatal(err) + } + bs := &Server{GenesisTimeFetcher: &mock.ChainService{}} + + wanted := "Cannot retrieve information about an epoch in the future" + if _, err := bs.GetValidatorActiveSetChanges( + ctx, + ðpb.GetValidatorActiveSetChangesRequest{ + QueryFilter: ðpb.GetValidatorActiveSetChangesRequest_Epoch{ + Epoch: helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1, + }, + }, + ); err != nil && !strings.Contains(err.Error(), wanted) { + t.Errorf("Expected error %v, received %v", wanted, err) + } +} + func TestServer_ListValidatorBalances_CannotRequestFutureEpoch(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) @@ -1123,36 +1144,10 @@ func TestServer_GetValidator(t *testing.T) { } } -func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing.T) { +func TestServer_GetValidatorActiveSetChanges(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) - ctx := context.Background() - st := testutil.NewBeaconState() - if err := st.SetSlot(0); err != nil { - t.Fatal(err) - } - bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: st, - }, - } - - wanted := "Cannot retrieve information about an epoch in the future" - if _, err := bs.GetValidatorActiveSetChanges( - ctx, - ðpb.GetValidatorActiveSetChangesRequest{ - QueryFilter: ðpb.GetValidatorActiveSetChangesRequest_Epoch{ - Epoch: 1, - }, - }, - ); err != nil && !strings.Contains(err.Error(), wanted) { - t.Errorf("Expected error %v, received %v", wanted, err) - } -} - -func TestServer_GetValidatorActiveSetChanges(t *testing.T) { ctx := context.Background() validators := make([]*ethpb.Validator, 8) headState := testutil.NewBeaconState() @@ -1197,15 +1192,32 @@ func TestServer_GetValidatorActiveSetChanges(t *testing.T) { t.Fatal(err) } } + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} + if err := db.SaveBlock(ctx, b); err != nil { + t.Fatal(err) + } + + gRoot, err := ssz.HashTreeRoot(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) + } + bs := &Server{ - HeadFetcher: &mock.ChainService{ - State: headState, - }, FinalizationFetcher: &mock.ChainService{ FinalizedCheckPoint: ðpb.Checkpoint{Epoch: 0}, }, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } - res, err := bs.GetValidatorActiveSetChanges(ctx, ðpb.GetValidatorActiveSetChangesRequest{}) + res, err := bs.GetValidatorActiveSetChanges(ctx, ðpb.GetValidatorActiveSetChangesRequest{ + QueryFilter: ðpb.GetValidatorActiveSetChangesRequest_Genesis{Genesis: true}, + }) if err != nil { t.Fatal(err) } @@ -1245,6 +1257,10 @@ func TestServer_GetValidatorActiveSetChanges(t *testing.T) { } func TestServer_GetValidatorActiveSetChanges_FromArchive(t *testing.T) { + fc := featureconfig.Get() + fc.DisableNewStateMgmt = true + featureconfig.Init(fc) + db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) ctx := context.Background()