diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index 884e86fc9da5..5a0970b36518 100644 --- a/beacon-chain/rpc/beacon/BUILD.bazel +++ b/beacon-chain/rpc/beacon/BUILD.bazel @@ -41,13 +41,11 @@ go_library( "//shared/bytesutil:go_default_library", "//shared/event:go_default_library", "//shared/featureconfig:go_default_library", - "//shared/hashutil:go_default_library", "//shared/pagination:go_default_library", "//shared/params:go_default_library", "//shared/sliceutil:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", "@com_github_patrickmn_go_cache//:go_default_library", - "@com_github_pkg_errors//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", diff --git a/beacon-chain/rpc/beacon/assignments.go b/beacon-chain/rpc/beacon/assignments.go index 144d93e351bc..26051c1b3217 100644 --- a/beacon-chain/rpc/beacon/assignments.go +++ b/beacon-chain/rpc/beacon/assignments.go @@ -4,15 +4,11 @@ import ( "context" "strconv" - "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/flags" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" - "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/pagination" - "github.com/prysmaticlabs/prysm/shared/params" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -32,14 +28,9 @@ func (bs *Server) ListValidatorAssignments( } var res []*ethpb.ValidatorAssignments_CommitteeAssignment - headState, err := bs.HeadFetcher.HeadState(ctx) - if err != nil { - return nil, status.Error(codes.Internal, "Could not get head state") - } filtered := map[uint64]bool{} // track filtered validators to prevent duplication in the response. filteredIndices := make([]uint64, 0) - requestedEpoch := helpers.CurrentEpoch(headState) - + var requestedEpoch uint64 switch q := req.QueryFilter.(type) { case *ethpb.ListValidatorAssignmentsRequest_Genesis: if q.Genesis { @@ -49,18 +40,24 @@ func (bs *Server) ListValidatorAssignments( requestedEpoch = q.Epoch } - if requestedEpoch > helpers.CurrentEpoch(headState) { + currentEpoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + if requestedEpoch > currentEpoch { return nil, status.Errorf( codes.InvalidArgument, "Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d", - helpers.CurrentEpoch(headState), + currentEpoch, requestedEpoch, ) } + requestedState, err := bs.StateGen.StateBySlot(ctx, helpers.StartSlot(requestedEpoch)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve archived state for epoch %d: %v", requestedEpoch, err) + } + // Filter out assignments by public keys. for _, pubKey := range req.PublicKeys { - index, ok := headState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) + index, ok := requestedState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { return nil, status.Errorf(codes.NotFound, "Could not find validator index for public key %#x", pubKey) } @@ -75,7 +72,7 @@ func (bs *Server) ListValidatorAssignments( } } - activeIndices, err := helpers.ActiveValidatorIndices(headState, requestedEpoch) + activeIndices, err := helpers.ActiveValidatorIndices(requestedState, requestedEpoch) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve active validator indices: %v", err) } @@ -96,53 +93,21 @@ func (bs *Server) ListValidatorAssignments( return nil, status.Errorf(codes.Internal, "Could not paginate results: %v", err) } - shouldFetchFromArchive := requestedEpoch < bs.FinalizationFetcher.FinalizedCheckpt().Epoch - - // initialize all committee related data. + // Initialize all committee related data. committeeAssignments := map[uint64]*helpers.CommitteeAssignmentContainer{} proposerIndexToSlots := make(map[uint64][]uint64) - archivedInfo := &pb.ArchivedCommitteeInfo{} - archivedBalances := make([]uint64, 0) - archivedAssignments := make(map[uint64]*ethpb.ValidatorAssignments_CommitteeAssignment) - - if shouldFetchFromArchive { - archivedInfo, archivedBalances, err = bs.archivedCommitteeData(ctx, requestedEpoch) - if err != nil { - return nil, err - } - archivedAssignments, err = archivedValidatorCommittee( - requestedEpoch, - archivedInfo, - activeIndices, - archivedBalances, - ) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not retrieve archived assignment for epoch %d: %v", requestedEpoch, err) - } - } else { - committeeAssignments, proposerIndexToSlots, err = helpers.CommitteeAssignments(headState, requestedEpoch) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err) - } + committeeAssignments, proposerIndexToSlots, err = helpers.CommitteeAssignments(requestedState, requestedEpoch) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err) } for _, index := range filteredIndices[start:end] { - if int(index) >= headState.NumValidators() { + if int(index) >= requestedState.NumValidators() { return nil, status.Errorf(codes.OutOfRange, "Validator index %d >= validator count %d", - index, headState.NumValidators()) - } - if shouldFetchFromArchive { - assignment, ok := archivedAssignments[index] - if !ok { - return nil, status.Errorf(codes.Internal, "Could not get archived committee assignment for index %d", index) - } - pubkey := headState.PubkeyAtIndex(index) - assignment.PublicKey = pubkey[:] - res = append(res, assignment) - continue + index, requestedState.NumValidators()) } comAssignment := committeeAssignments[index] - pubkey := headState.PubkeyAtIndex(index) + pubkey := requestedState.PubkeyAtIndex(index) assign := ðpb.ValidatorAssignments_CommitteeAssignment{ BeaconCommittees: comAssignment.Committee, CommitteeIndex: comAssignment.CommitteeIndex, @@ -160,93 +125,3 @@ func (bs *Server) ListValidatorAssignments( TotalSize: int32(len(filteredIndices)), }, nil } - -// Computes validator assignments for an epoch and validator index using archived committee -// information, archived balances, and a set of active validators. -func archivedValidatorCommittee( - epoch uint64, - archivedInfo *pb.ArchivedCommitteeInfo, - activeIndices []uint64, - archivedBalances []uint64, -) (map[uint64]*ethpb.ValidatorAssignments_CommitteeAssignment, error) { - proposerSeed := bytesutil.ToBytes32(archivedInfo.ProposerSeed) - attesterSeed := bytesutil.ToBytes32(archivedInfo.AttesterSeed) - - startSlot := helpers.StartSlot(epoch) - proposerIndexToSlots := make(map[uint64][]uint64) - activeVals := make([]*ethpb.Validator, len(archivedBalances)) - for i, bal := range archivedBalances { - activeVals[i] = ðpb.Validator{EffectiveBalance: bal} - } - - for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { - seedWithSlot := append(proposerSeed[:], bytesutil.Bytes8(slot)...) - seedWithSlotHash := hashutil.Hash(seedWithSlot) - i, err := helpers.ComputeProposerIndex(activeVals, activeIndices, seedWithSlotHash) - if err != nil { - return nil, errors.Wrapf(err, "could not check proposer at slot %d", slot) - } - proposerIndexToSlots[i] = append(proposerIndexToSlots[i], slot) - } - - assignmentMap := make(map[uint64]*ethpb.ValidatorAssignments_CommitteeAssignment) - for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { - var countAtSlot = uint64(len(activeIndices)) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize - if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot { - countAtSlot = params.BeaconConfig().MaxCommitteesPerSlot - } - if countAtSlot == 0 { - countAtSlot = 1 - } - for i := uint64(0); i < countAtSlot; i++ { - committee, err := helpers.BeaconCommittee(activeIndices, attesterSeed, slot, i) - if err != nil { - return nil, errors.Wrap(err, "could not compute committee") - } - for _, index := range committee { - assignmentMap[index] = ðpb.ValidatorAssignments_CommitteeAssignment{ - BeaconCommittees: committee, - CommitteeIndex: i, - AttesterSlot: slot, - ProposerSlots: proposerIndexToSlots[index], - } - } - } - } - return assignmentMap, nil -} - -func (bs *Server) archivedCommitteeData(ctx context.Context, requestedEpoch uint64) (*pb.ArchivedCommitteeInfo, - []uint64, error) { - archivedInfo, err := bs.BeaconDB.ArchivedCommitteeInfo(ctx, requestedEpoch) - if err != nil { - return nil, nil, status.Errorf( - codes.Internal, - "Could not retrieve archived committee info for epoch %d", - requestedEpoch, - ) - } - if archivedInfo == nil { - return nil, nil, status.Errorf( - codes.NotFound, - "Could not retrieve data for epoch %d, perhaps --archive in the running beacon node is disabled", - requestedEpoch, - ) - } - archivedBalances, err := bs.BeaconDB.ArchivedBalances(ctx, requestedEpoch) - if err != nil { - return nil, nil, status.Errorf( - codes.Internal, - "Could not retrieve archived balances for epoch %d", - requestedEpoch, - ) - } - if archivedBalances == nil { - return nil, nil, status.Errorf( - codes.NotFound, - "Could not retrieve data for epoch %d, perhaps --archive in the running beacon node is disabled", - requestedEpoch, - ) - } - return archivedInfo, archivedBalances, nil -} diff --git a/beacon-chain/rpc/beacon/assignments_test.go b/beacon-chain/rpc/beacon/assignments_test.go index 11bb7e19f1c9..24c262cc8e67 100644 --- a/beacon-chain/rpc/beacon/assignments_test.go +++ b/beacon-chain/rpc/beacon/assignments_test.go @@ -13,11 +13,11 @@ 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/cache" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/beacon-chain/flags" - stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" - pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" ) @@ -27,17 +27,9 @@ func TestServer_ListAssignments_CannotRequestFutureEpoch(t *testing.T) { defer dbTest.TeardownDB(t, db) ctx := context.Background() - st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{ - Slot: 0, - }) - if err != nil { - t.Fatal(err) - } bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: st, - }, + BeaconDB: db, + GenesisTimeFetcher: &mock.ChainService{}, } wanted := "Cannot retrieve information about an epoch in the future" @@ -45,7 +37,7 @@ func TestServer_ListAssignments_CannotRequestFutureEpoch(t *testing.T) { ctx, ðpb.ListValidatorAssignmentsRequest{ QueryFilter: ðpb.ListValidatorAssignmentsRequest_Epoch{ - Epoch: 1, + Epoch: helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1, }, }, ); err != nil && !strings.Contains(err.Error(), wanted) { @@ -58,19 +50,27 @@ func TestServer_ListAssignments_NoResults(t *testing.T) { defer dbTest.TeardownDB(t, db) ctx := context.Background() - st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{ - Slot: 0, - RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), - Validators: []*ethpb.Validator{}, - }) + st := testutil.NewBeaconState() + + 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, st, gRoot); err != nil { + t.Fatal(err) + } + bs := &Server{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: st, - }, + BeaconDB: db, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } wanted := ðpb.ValidatorAssignments{ Assignments: make([]*ethpb.ValidatorAssignments_CommitteeAssignment, 0), @@ -103,11 +103,26 @@ func TestServer_ListAssignments_Pagination_InputOutOfRange(t *testing.T) { if err != nil { 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{ - BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: headState, - }, + BeaconDB: db, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } wanted := fmt.Sprintf("page start %d >= list %d", 0, 0) @@ -179,7 +194,7 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_NoArchive(t *testing. if err := db.SaveState(ctx, s, blockRoot); err != nil { t.Fatal(err) } - if err := db.SaveHeadBlockRoot(ctx, blockRoot); err != nil { + if err := db.SaveGenesisBlockRoot(ctx, blockRoot); err != nil { t.Fatal(err) } @@ -193,6 +208,8 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_NoArchive(t *testing. Epoch: 0, }, }, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } res, err := bs.ListValidatorAssignments(context.Background(), ðpb.ListValidatorAssignmentsRequest{ @@ -277,7 +294,7 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_FromArchive(t *testin if err := db.SaveState(ctx, s, blockRoot); err != nil { t.Fatal(err) } - if err := db.SaveHeadBlockRoot(ctx, blockRoot); err != nil { + if err := db.SaveGenesisBlockRoot(ctx, blockRoot); err != nil { t.Fatal(err) } @@ -285,35 +302,13 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_FromArchive(t *testin // we request assignments for epoch 0, it looks within the archived data. bs := &Server{ BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: s, - }, FinalizationFetcher: &mock.ChainService{ FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 10, }, }, - } - - // We then store archived data into the DB. - currentEpoch := helpers.CurrentEpoch(s) - proposerSeed, err := helpers.Seed(s, currentEpoch, params.BeaconConfig().DomainBeaconProposer) - if err != nil { - t.Fatal(err) - } - attesterSeed, err := helpers.Seed(s, currentEpoch, params.BeaconConfig().DomainBeaconAttester) - if err != nil { - t.Fatal(err) - } - if err := db.SaveArchivedCommitteeInfo(context.Background(), 0, &pbp2p.ArchivedCommitteeInfo{ - ProposerSeed: proposerSeed[:], - AttesterSeed: attesterSeed[:], - }); err != nil { - t.Fatal(err) - } - - if err := db.SaveArchivedBalances(context.Background(), 0, balances); err != nil { - t.Fatal(err) + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } // Construct the wanted assignments. @@ -379,20 +374,19 @@ func TestServer_ListAssignments_FilterPubkeysIndices_NoPagination(t *testing.T) if err := db.SaveState(ctx, s, blockRoot); err != nil { t.Fatal(err) } - if err := db.SaveHeadBlockRoot(ctx, blockRoot); err != nil { + if err := db.SaveGenesisBlockRoot(ctx, blockRoot); err != nil { t.Fatal(err) } bs := &Server{ BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: s, - }, FinalizationFetcher: &mock.ChainService{ FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 0, }, }, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } pubKey1 := make([]byte, params.BeaconConfig().BLSPubkeyLength) @@ -462,20 +456,19 @@ func TestServer_ListAssignments_CanFilterPubkeysIndices_WithPagination(t *testin if err := db.SaveState(ctx, s, blockRoot); err != nil { t.Fatal(err) } - if err := db.SaveHeadBlockRoot(ctx, blockRoot); err != nil { + if err := db.SaveGenesisBlockRoot(ctx, blockRoot); err != nil { t.Fatal(err) } bs := &Server{ BeaconDB: db, - HeadFetcher: &mock.ChainService{ - State: s, - }, FinalizationFetcher: &mock.ChainService{ FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 0, }, }, + GenesisTimeFetcher: &mock.ChainService{}, + StateGen: stategen.New(db, cache.NewStateSummaryCache()), } req := ðpb.ListValidatorAssignmentsRequest{Indices: []uint64{1, 2, 3, 4, 5, 6}, PageSize: 2, PageToken: "1"} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index e499f86d11cc..cd9bf531469b 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -259,9 +259,9 @@ func (s *Service) Start() { BlockNotifier: s.blockNotifier, AttestationNotifier: s.operationNotifier, Broadcaster: s.p2p, + StateGen: s.stateGen, ReceivedAttestationsBuffer: make(chan *ethpb.Attestation, 100), CollectedAttestationsBuffer: make(chan []*ethpb.Attestation, 100), - StateGen: s.stateGen, } ethpb.RegisterNodeServer(s.grpcServer, nodeServer) ethpb.RegisterBeaconChainServer(s.grpcServer, beaconChainServer)