diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index 07df694496..c1faa77ef1 100644 --- a/beacon-chain/rpc/beacon/BUILD.bazel +++ b/beacon-chain/rpc/beacon/BUILD.bazel @@ -75,7 +75,6 @@ go_test( deps = [ "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/cache:go_default_library", - "//beacon-chain/core/epoch/precompute:go_default_library", "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/block:go_default_library", "//beacon-chain/core/feed/operation:go_default_library", diff --git a/beacon-chain/rpc/beacon/validators.go b/beacon-chain/rpc/beacon/validators.go index 1616db8c62..d139713697 100644 --- a/beacon-chain/rpc/beacon/validators.go +++ b/beacon-chain/rpc/beacon/validators.go @@ -13,6 +13,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/beacon-chain/flags" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/pagination" "github.com/prysmaticlabs/prysm/shared/params" "google.golang.org/grpc/codes" @@ -462,6 +463,68 @@ func (bs *Server) GetValidatorActiveSetChanges( func (bs *Server) GetValidatorParticipation( ctx context.Context, req *ethpb.GetValidatorParticipationRequest, ) (*ethpb.ValidatorParticipationResponse, error) { + + if featureconfig.Get().DisableNewStateMgmt { + return bs.getValidatorParticipationUsingOldArchival(ctx, req) + } + + currentEpoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + + var requestedEpoch uint64 + switch q := req.QueryFilter.(type) { + case *ethpb.GetValidatorParticipationRequest_Genesis: + requestedEpoch = 0 + case *ethpb.GetValidatorParticipationRequest_Epoch: + requestedEpoch = q.Epoch + default: + // Prevent underflow and ensure participation is always queried for previous epoch. + if currentEpoch > 1 { + requestedEpoch = currentEpoch - 1 + } + } + + if requestedEpoch >= currentEpoch { + return nil, status.Errorf( + codes.InvalidArgument, + "Cannot retrieve information about an epoch until older than current epoch, current epoch %d, requesting %d", + currentEpoch, + requestedEpoch, + ) + } + + requestedState, err := bs.StateGen.StateBySlot(ctx, helpers.StartSlot(requestedEpoch+1)) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get state") + } + + v, b, err := precompute.New(ctx, requestedState) + if err != nil { + return nil, status.Error(codes.Internal, "Could not set up pre compute instance") + } + _, b, err = precompute.ProcessAttestations(ctx, requestedState, v, b) + if err != nil { + return nil, status.Error(codes.Internal, "Could not pre compute attestations") + } + + headState, err := bs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get head state") + } + + return ðpb.ValidatorParticipationResponse{ + Epoch: requestedEpoch, + Finalized: requestedEpoch <= headState.FinalizedCheckpointEpoch(), + Participation: ðpb.ValidatorParticipation{ + GlobalParticipationRate: float32(b.PrevEpochTargetAttesters) / float32(b.PrevEpoch), + VotedEther: b.PrevEpochTargetAttesters, + EligibleEther: b.PrevEpoch, + }, + }, nil +} + +func (bs *Server) getValidatorParticipationUsingOldArchival( + ctx context.Context, req *ethpb.GetValidatorParticipationRequest, +) (*ethpb.ValidatorParticipationResponse, 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 98b92a0da9..78ecc2d112 100644 --- a/beacon-chain/rpc/beacon/validators_test.go +++ b/beacon-chain/rpc/beacon/validators_test.go @@ -15,13 +15,15 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" - "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute" + "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/beacon-chain/flags" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" ) @@ -1547,6 +1549,10 @@ func TestServer_GetValidatorQueue_PendingExit(t *testing.T) { } func TestServer_GetValidatorParticipation_CannotRequestCurrentEpoch(t *testing.T) { + fc := featureconfig.Get() + fc.DisableNewStateMgmt = true + featureconfig.Init(fc) + db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) @@ -1589,6 +1595,7 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) HeadFetcher: &mock.ChainService{ State: headState, }, + GenesisTimeFetcher: &mock.ChainService{}, } wanted := "Cannot retrieve information about an epoch in the future" @@ -1596,7 +1603,7 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) ctx, ðpb.GetValidatorParticipationRequest{ QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: 1, + Epoch: helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1, }, }, ); err != nil && !strings.Contains(err.Error(), wanted) { @@ -1605,6 +1612,10 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) } func TestServer_GetValidatorParticipation_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() @@ -1665,7 +1676,116 @@ func TestServer_GetValidatorParticipation_FromArchive(t *testing.T) { } } +func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + + ctx := context.Background() + validatorCount := uint64(100) + + validators := make([]*ethpb.Validator, validatorCount) + balances := make([]uint64, validatorCount) + for i := 0; i < len(validators); i++ { + validators[i] = ðpb.Validator{ + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, + } + balances[i] = params.BeaconConfig().MaxEffectiveBalance + } + + atts := []*pbp2p.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}} + headState := testutil.NewBeaconState() + if err := headState.SetSlot(params.BeaconConfig().SlotsPerEpoch); err != nil { + t.Fatal(err) + } + if err := headState.SetValidators(validators); err != nil { + t.Fatal(err) + } + if err := headState.SetBalances(balances); err != nil { + t.Fatal(err) + } + if err := headState.SetPreviousEpochAttestations(atts); err != nil { + t.Fatal(err) + } + + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: params.BeaconConfig().SlotsPerEpoch}} + if err := db.SaveBlock(ctx, b); err != nil { + t.Fatal(err) + } + bRoot, err := ssz.HashTreeRoot(b.Block) + if err != nil { + t.Fatal(err) + } + if err := db.SaveState(ctx, headState, bRoot); err != nil { + t.Fatal(err) + } + + m := &mock.ChainService{State: headState} + bs := &Server{ + BeaconDB: db, + HeadFetcher: m, + ParticipationFetcher: m, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), + } + + res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 0}}) + if err != nil { + t.Fatal(err) + } + + wanted := ðpb.ValidatorParticipation{EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance} + if !reflect.DeepEqual(res.Participation, wanted) { + t.Error("Incorrect validator participation respond") + } +} + +func TestServer_GetValidatorParticipation_DoesntExist(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + ctx := context.Background() + + headState := testutil.NewBeaconState() + if err := headState.SetSlot(params.BeaconConfig().SlotsPerEpoch); err != nil { + t.Fatal(err) + } + + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: params.BeaconConfig().SlotsPerEpoch}} + if err := db.SaveBlock(ctx, b); err != nil { + t.Fatal(err) + } + bRoot, err := ssz.HashTreeRoot(b.Block) + if err != nil { + t.Fatal(err) + } + if err := db.SaveState(ctx, headState, bRoot); err != nil { + t.Fatal(err) + } + + m := &mock.ChainService{State: headState} + bs := &Server{ + BeaconDB: db, + HeadFetcher: m, + ParticipationFetcher: m, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), + } + + res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 0}}) + if err != nil { + t.Fatal(err) + } + + if res.Participation.VotedEther != 0 || res.Participation.EligibleEther != 0 { + t.Error("Incorrect validator participation response") + } +} + func TestServer_GetValidatorParticipation_FromArchive_FinalizedEpoch(t *testing.T) { + fc := featureconfig.Get() + fc.DisableNewStateMgmt = true + featureconfig.Init(fc) + db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) ctx := context.Background() @@ -1713,127 +1833,6 @@ func TestServer_GetValidatorParticipation_FromArchive_FinalizedEpoch(t *testing. } } -func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) { - db := dbTest.SetupDB(t) - defer dbTest.TeardownDB(t, db) - - ctx := context.Background() - epoch := uint64(1) - attestedBalance := uint64(1) - validatorCount := uint64(100) - - validators := make([]*ethpb.Validator, validatorCount) - balances := make([]uint64, validatorCount) - for i := 0; i < len(validators); i++ { - validators[i] = ðpb.Validator{ - ExitEpoch: params.BeaconConfig().FarFutureEpoch, - EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, - } - balances[i] = params.BeaconConfig().MaxEffectiveBalance - } - - atts := []*pbp2p.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}} - headState := testutil.NewBeaconState() - if err := headState.SetSlot(epoch*params.BeaconConfig().SlotsPerEpoch + 1); err != nil { - t.Fatal(err) - } - if err := headState.SetValidators(validators); err != nil { - t.Fatal(err) - } - if err := headState.SetBalances(balances); err != nil { - t.Fatal(err) - } - if err := headState.SetCurrentEpochAttestations(atts); err != nil { - t.Fatal(err) - } - - m := &mock.ChainService{ - State: headState, - Balance: &precompute.Balance{ - PrevEpoch: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PrevEpochTargetAttesters: attestedBalance, - }, - } - bs := &Server{ - BeaconDB: db, - HeadFetcher: m, - ParticipationFetcher: m, - } - - res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{}) - if err != nil { - t.Fatal(err) - } - - wanted := ðpb.ValidatorParticipation{ - VotedEther: attestedBalance, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - GlobalParticipationRate: float32(attestedBalance) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance), - } - - if !reflect.DeepEqual(res.Participation, wanted) { - t.Error("Incorrect validator participation respond") - } -} - -func TestServer_GetValidatorParticipation_DoesntExist(t *testing.T) { - db := dbTest.SetupDB(t) - defer dbTest.TeardownDB(t, db) - - ctx := context.Background() - epoch := uint64(1) - validatorCount := uint64(100) - - validators := make([]*ethpb.Validator, validatorCount) - balances := make([]uint64, validatorCount) - for i := 0; i < len(validators); i++ { - validators[i] = ðpb.Validator{ - ExitEpoch: params.BeaconConfig().FarFutureEpoch, - EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, - } - balances[i] = params.BeaconConfig().MaxEffectiveBalance - } - - atts := []*pbp2p.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}} - headState := testutil.NewBeaconState() - if err := headState.SetSlot(epoch*params.BeaconConfig().SlotsPerEpoch + 1); err != nil { - t.Fatal(err) - } - if err := headState.SetValidators(validators); err != nil { - t.Fatal(err) - } - if err := headState.SetBalances(balances); err != nil { - t.Fatal(err) - } - if err := headState.SetCurrentEpochAttestations(atts); err != nil { - t.Fatal(err) - } - - m := &mock.ChainService{ - State: headState, - } - bs := &Server{ - BeaconDB: db, - HeadFetcher: m, - ParticipationFetcher: m, - } - - res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{}) - if err != nil { - t.Fatal(err) - } - - wanted := ðpb.ValidatorParticipation{ - GlobalParticipationRate: 0, - VotedEther: 0, - EligibleEther: 0, - } - - if !reflect.DeepEqual(res.Participation, wanted) { - t.Errorf("Incorrect validator participation response, got %s", res.Participation.String()) - } -} - func BenchmarkListValidatorBalances(b *testing.B) { b.StopTimer() db := dbTest.SetupDB(b)