package beacon import ( "context" "strconv" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/cmd" "github.com/prysmaticlabs/prysm/shared/pagination" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // ListValidatorAssignments retrieves the validator assignments for a given epoch, // optional validator indices or public keys may be included to filter validator assignments. func (bs *Server) ListValidatorAssignments( ctx context.Context, req *ethpb.ListValidatorAssignmentsRequest, ) (*ethpb.ValidatorAssignments, error) { if int(req.PageSize) > cmd.Get().MaxRPCPageSize { return nil, status.Errorf( codes.InvalidArgument, "Requested page size %d can not be greater than max size %d", req.PageSize, cmd.Get().MaxRPCPageSize, ) } var res []*ethpb.ValidatorAssignments_CommitteeAssignment filtered := map[uint64]bool{} // track filtered validators to prevent duplication in the response. filteredIndices := make([]uint64, 0) var requestedEpoch uint64 switch q := req.QueryFilter.(type) { case *ethpb.ListValidatorAssignmentsRequest_Genesis: if q.Genesis { requestedEpoch = 0 } case *ethpb.ListValidatorAssignmentsRequest_Epoch: requestedEpoch = q.Epoch } 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", 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 := requestedState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) if !ok { return nil, status.Errorf(codes.NotFound, "Could not find validator index for public key %#x", pubKey) } filtered[index] = true filteredIndices = append(filteredIndices, index) } // Filter out assignments by validator indices. for _, index := range req.Indices { if !filtered[index] { filteredIndices = append(filteredIndices, index) } } activeIndices, err := helpers.ActiveValidatorIndices(requestedState, requestedEpoch) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve active validator indices: %v", err) } if len(filteredIndices) == 0 { if len(activeIndices) == 0 { return ðpb.ValidatorAssignments{ Assignments: make([]*ethpb.ValidatorAssignments_CommitteeAssignment, 0), TotalSize: int32(0), NextPageToken: strconv.Itoa(0), }, nil } // If no filter was specified, return assignments from active validator indices with pagination. filteredIndices = activeIndices } start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), len(filteredIndices)) if err != nil { return nil, status.Errorf(codes.Internal, "Could not paginate results: %v", err) } // Initialize all committee related data. committeeAssignments := map[uint64]*helpers.CommitteeAssignmentContainer{} proposerIndexToSlots := make(map[uint64][]uint64) 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 index >= uint64(requestedState.NumValidators()) { return nil, status.Errorf(codes.OutOfRange, "Validator index %d >= validator count %d", index, requestedState.NumValidators()) } comAssignment := committeeAssignments[index] pubkey := requestedState.PubkeyAtIndex(index) assign := ðpb.ValidatorAssignments_CommitteeAssignment{ BeaconCommittees: comAssignment.Committee, CommitteeIndex: comAssignment.CommitteeIndex, AttesterSlot: comAssignment.AttesterSlot, ProposerSlots: proposerIndexToSlots[index], PublicKey: pubkey[:], ValidatorIndex: index, } res = append(res, assign) } return ðpb.ValidatorAssignments{ Epoch: requestedEpoch, Assignments: res, NextPageToken: nextPageToken, TotalSize: int32(len(filteredIndices)), }, nil }