diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index 255efd72dcbf..884e86fc9da5 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 1616db8c62b0..cd41b3a3eb69 100644 --- a/beacon-chain/rpc/beacon/validators.go +++ b/beacon-chain/rpc/beacon/validators.go @@ -462,81 +462,57 @@ func (bs *Server) GetValidatorActiveSetChanges( func (bs *Server) GetValidatorParticipation( 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") - } - - currentEpoch := helpers.CurrentEpoch(headState) - prevEpoch := helpers.PrevEpoch(headState) + currentEpoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) var requestedEpoch uint64 - var requestingGenesis bool switch q := req.QueryFilter.(type) { case *ethpb.GetValidatorParticipationRequest_Genesis: - requestingGenesis = q.Genesis requestedEpoch = 0 case *ethpb.GetValidatorParticipationRequest_Epoch: requestedEpoch = q.Epoch default: - requestedEpoch = prevEpoch + // Prevent underflow and ensure participation is always queried for previous epoch. + if currentEpoch > 1 { + requestedEpoch = currentEpoch - 1 + } } - // If the request is from genesis or another past epoch, we look into our archived - // data to find it and return it if it exists. - if requestingGenesis || requestedEpoch < prevEpoch { - participation, err := bs.BeaconDB.ArchivedValidatorParticipation(ctx, requestedEpoch) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not fetch archived participation: %v", err) - } - if participation == nil { - return nil, status.Errorf( - codes.NotFound, - "Could not retrieve data for epoch %d, perhaps --archive in the running beacon node is disabled", - 0, - ) - } - return ðpb.ValidatorParticipationResponse{ - Epoch: requestedEpoch, - Finalized: requestedEpoch <= headState.FinalizedCheckpointEpoch(), - Participation: participation, - }, nil - } else if requestedEpoch == currentEpoch { - // We cannot retrieve participation for an epoch currently in progress. + if requestedEpoch >= currentEpoch { return nil, status.Errorf( codes.InvalidArgument, - "Cannot retrieve information about an epoch currently in progress, current epoch %d, requesting %d", - currentEpoch, - requestedEpoch, - ) - } else if requestedEpoch > currentEpoch { - // 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", + "Cannot retrieve information about an epoch until older than current epoch, current epoch %d, requesting %d", currentEpoch, requestedEpoch, ) } - p := bs.ParticipationFetcher.Participation(requestedEpoch) - if p == nil { - p = &precompute.Balance{} + requestedState, err := bs.StateGen.StateBySlot(ctx, helpers.StartSlot(requestedEpoch+1)) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get state") } - participation := ðpb.ValidatorParticipation{ - EligibleEther: p.PrevEpoch, - VotedEther: p.PrevEpochTargetAttesters, + + 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") } - participation.GlobalParticipationRate = float32(0) - // only divide if prevEpoch is non zero - if p.PrevEpoch != 0 { - participation.GlobalParticipationRate = float32(p.PrevEpochTargetAttesters) / float32(p.PrevEpoch) + + 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: participation, + Epoch: requestedEpoch, + Finalized: requestedEpoch <= headState.FinalizedCheckpointEpoch(), + Participation: ðpb.ValidatorParticipation{ + GlobalParticipationRate: float32(b.PrevEpochTargetAttesters) / float32(b.PrevEpoch), + VotedEther: b.PrevEpochTargetAttesters, + EligibleEther: b.PrevEpoch, + }, }, nil } diff --git a/beacon-chain/rpc/beacon/validators_test.go b/beacon-chain/rpc/beacon/validators_test.go index 98b92a0da933..773b475057fc 100644 --- a/beacon-chain/rpc/beacon/validators_test.go +++ b/beacon-chain/rpc/beacon/validators_test.go @@ -15,12 +15,13 @@ 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/params" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -1546,35 +1547,6 @@ func TestServer_GetValidatorQueue_PendingExit(t *testing.T) { } } -func TestServer_GetValidatorParticipation_CannotRequestCurrentEpoch(t *testing.T) { - db := dbTest.SetupDB(t) - defer dbTest.TeardownDB(t, db) - - ctx := context.Background() - headState := testutil.NewBeaconState() - if err := headState.SetSlot(helpers.StartSlot(2)); err != nil { - t.Fatal(err) - } - bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: headState, - }, - } - - wanted := "Cannot retrieve information about an epoch currently in progress" - if _, err := bs.GetValidatorParticipation( - ctx, - ðpb.GetValidatorParticipationRequest{ - QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: 2, - }, - }, - ); err != nil && !strings.Contains(err.Error(), wanted) { - t.Errorf("Expected error %v, received %v", wanted, err) - } -} - func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) @@ -1589,14 +1561,15 @@ 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" + wanted := "Cannot retrieve information about an epoch until older than current epoch" if _, err := bs.GetValidatorParticipation( ctx, ðpb.GetValidatorParticipationRequest{ QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: 1, + Epoch: helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1, }, }, ); err != nil && !strings.Contains(err.Error(), wanted) { @@ -1604,122 +1577,11 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) } } -func TestServer_GetValidatorParticipation_FromArchive(t *testing.T) { - db := dbTest.SetupDB(t) - defer dbTest.TeardownDB(t, db) - ctx := context.Background() - epoch := uint64(4) - part := ðpb.ValidatorParticipation{ - GlobalParticipationRate: 1.0, - VotedEther: 20, - EligibleEther: 20, - } - if err := db.SaveArchivedValidatorParticipation(ctx, epoch-2, part); err != nil { - t.Fatal(err) - } - - headState := testutil.NewBeaconState() - if err := headState.SetSlot(helpers.StartSlot(epoch + 1)); err != nil { - t.Fatal(err) - } - if err := headState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: epoch + 1}); err != nil { - t.Fatal(err) - } - bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: headState, - }, - } - if _, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{ - QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: epoch + 2, - }, - }); err == nil { - t.Error("Expected error when requesting future epoch, received nil") - } - // We request data from epoch 0, which we didn't archive, so we should expect an error. - if _, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{ - QueryFilter: ðpb.GetValidatorParticipationRequest_Genesis{ - Genesis: true, - }, - }); err == nil { - t.Error("Expected error when data from archive is not found, received nil") - } - - want := ðpb.ValidatorParticipationResponse{ - Epoch: epoch - 2, - Finalized: true, - Participation: part, - } - res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{ - QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: epoch - 2, - }, - }) - if err != nil { - t.Fatal(err) - } - if !proto.Equal(want, res) { - t.Errorf("Wanted %v, received %v", want, res) - } -} - -func TestServer_GetValidatorParticipation_FromArchive_FinalizedEpoch(t *testing.T) { - db := dbTest.SetupDB(t) - defer dbTest.TeardownDB(t, db) - ctx := context.Background() - part := ðpb.ValidatorParticipation{ - GlobalParticipationRate: 1.0, - VotedEther: 20, - EligibleEther: 20, - } - epoch := uint64(1) - // We archive data for epoch 1. - if err := db.SaveArchivedValidatorParticipation(ctx, epoch, part); err != nil { - t.Fatal(err) - } - headState := testutil.NewBeaconState() - if err := headState.SetSlot(helpers.StartSlot(epoch + 10)); err != nil { - t.Fatal(err) - } - if err := headState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: epoch + 5}); err != nil { - t.Fatal(err) - } - - bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - // 10 epochs into the future. - State: headState, - }, - } - want := ðpb.ValidatorParticipationResponse{ - Epoch: epoch, - Finalized: true, - Participation: part, - } - // We request epoch 1. - res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{ - QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{ - Epoch: epoch, - }, - }) - if err != nil { - t.Fatal(err) - } - if !proto.Equal(want, res) { - t.Errorf("Wanted %v, received %v", want, res) - } -} - 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) @@ -1734,7 +1596,7 @@ func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) { atts := []*pbp2p.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}} headState := testutil.NewBeaconState() - if err := headState.SetSlot(epoch*params.BeaconConfig().SlotsPerEpoch + 1); err != nil { + if err := headState.SetSlot(params.BeaconConfig().SlotsPerEpoch); err != nil { t.Fatal(err) } if err := headState.SetValidators(validators); err != nil { @@ -1743,34 +1605,37 @@ func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) { if err := headState.SetBalances(balances); err != nil { t.Fatal(err) } - if err := headState.SetCurrentEpochAttestations(atts); err != nil { + if err := headState.SetPreviousEpochAttestations(atts); err != nil { t.Fatal(err) } - m := &mock.ChainService{ - State: headState, - Balance: &precompute.Balance{ - PrevEpoch: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PrevEpochTargetAttesters: attestedBalance, - }, + 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{}) + res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 0}}) if err != nil { t.Fatal(err) } - wanted := ðpb.ValidatorParticipation{ - VotedEther: attestedBalance, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - GlobalParticipationRate: float32(attestedBalance) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance), - } - + wanted := ðpb.ValidatorParticipation{EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance} if !reflect.DeepEqual(res.Participation, wanted) { t.Error("Incorrect validator participation respond") } @@ -1779,58 +1644,41 @@ func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) { 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 { + if err := headState.SetSlot(params.BeaconConfig().SlotsPerEpoch); err != nil { t.Fatal(err) } - if err := headState.SetValidators(validators); err != nil { + + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: params.BeaconConfig().SlotsPerEpoch}} + if err := db.SaveBlock(ctx, b); err != nil { t.Fatal(err) } - if err := headState.SetBalances(balances); err != nil { + bRoot, err := ssz.HashTreeRoot(b.Block) + if err != nil { t.Fatal(err) } - if err := headState.SetCurrentEpochAttestations(atts); err != nil { + if err := db.SaveState(ctx, headState, bRoot); err != nil { t.Fatal(err) } - m := &mock.ChainService{ - State: headState, - } + 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{}) + res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 0}}) 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()) + if res.Participation.VotedEther != 0 || res.Participation.EligibleEther != 0 { + t.Error("Incorrect validator participation response") } }