github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/beacon/committees.go (about)

     1  package beacon
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
     9  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    10  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    11  	"github.com/prysmaticlabs/prysm/shared/params"
    12  	"google.golang.org/grpc/codes"
    13  	"google.golang.org/grpc/status"
    14  )
    15  
    16  // ListBeaconCommittees for a given epoch.
    17  //
    18  // If no filter criteria is specified, the response returns
    19  // all beacon committees for the current epoch. The results are paginated by default.
    20  func (bs *Server) ListBeaconCommittees(
    21  	ctx context.Context,
    22  	req *ethpb.ListCommitteesRequest,
    23  ) (*ethpb.BeaconCommittees, error) {
    24  	currentSlot := bs.GenesisTimeFetcher.CurrentSlot()
    25  	var requestedSlot types.Slot
    26  	switch q := req.QueryFilter.(type) {
    27  	case *ethpb.ListCommitteesRequest_Epoch:
    28  		startSlot, err := helpers.StartSlot(q.Epoch)
    29  		if err != nil {
    30  			return nil, err
    31  		}
    32  		requestedSlot = startSlot
    33  	case *ethpb.ListCommitteesRequest_Genesis:
    34  		requestedSlot = 0
    35  	default:
    36  		requestedSlot = currentSlot
    37  	}
    38  
    39  	requestedEpoch := helpers.SlotToEpoch(requestedSlot)
    40  	currentEpoch := helpers.SlotToEpoch(currentSlot)
    41  	if requestedEpoch > currentEpoch {
    42  		return nil, status.Errorf(
    43  			codes.InvalidArgument,
    44  			"Cannot retrieve information for an future epoch, current epoch %d, requesting %d",
    45  			currentEpoch,
    46  			requestedEpoch,
    47  		)
    48  	}
    49  
    50  	committees, activeIndices, err := bs.retrieveCommitteesForEpoch(ctx, requestedEpoch)
    51  	if err != nil {
    52  		return nil, status.Errorf(
    53  			codes.Internal,
    54  			"Could not retrieve committees for epoch %d: %v",
    55  			requestedEpoch,
    56  			err,
    57  		)
    58  	}
    59  
    60  	return &ethpb.BeaconCommittees{
    61  		Epoch:                requestedEpoch,
    62  		Committees:           committees.SlotToUint64(),
    63  		ActiveValidatorCount: uint64(len(activeIndices)),
    64  	}, nil
    65  }
    66  
    67  func (bs *Server) retrieveCommitteesForEpoch(
    68  	ctx context.Context,
    69  	epoch types.Epoch,
    70  ) (SlotToCommiteesMap, []types.ValidatorIndex, error) {
    71  	startSlot, err := helpers.StartSlot(epoch)
    72  	if err != nil {
    73  		return nil, nil, err
    74  	}
    75  	requestedState, err := bs.StateGen.StateBySlot(ctx, startSlot)
    76  	if err != nil {
    77  		return nil, nil, status.Errorf(codes.Internal, "Could not get state: %v", err)
    78  	}
    79  	seed, err := helpers.Seed(requestedState, epoch, params.BeaconConfig().DomainBeaconAttester)
    80  	if err != nil {
    81  		return nil, nil, status.Error(codes.Internal, "Could not get seed")
    82  	}
    83  	activeIndices, err := helpers.ActiveValidatorIndices(requestedState, epoch)
    84  	if err != nil {
    85  		return nil, nil, status.Error(codes.Internal, "Could not get active indices")
    86  	}
    87  
    88  	committeesListsBySlot, err := computeCommittees(startSlot, activeIndices, seed)
    89  	if err != nil {
    90  		return nil, nil, status.Errorf(
    91  			codes.InvalidArgument,
    92  			"Could not compute committees for epoch %d: %v",
    93  			helpers.SlotToEpoch(startSlot),
    94  			err,
    95  		)
    96  	}
    97  	return committeesListsBySlot, activeIndices, nil
    98  }
    99  
   100  // retrieveCommitteesForRoot uses the provided state root to get the current epoch committees.
   101  // Note: This function is always recommended over retrieveCommitteesForEpoch as states are
   102  // retrieved from the DB for this function, rather than generated.
   103  func (bs *Server) retrieveCommitteesForRoot(
   104  	ctx context.Context,
   105  	root []byte,
   106  ) (SlotToCommiteesMap, []types.ValidatorIndex, error) {
   107  	requestedState, err := bs.StateGen.StateByRoot(ctx, bytesutil.ToBytes32(root))
   108  	if err != nil {
   109  		return nil, nil, status.Error(codes.Internal, fmt.Sprintf("Could not get state: %v", err))
   110  	}
   111  	epoch := helpers.CurrentEpoch(requestedState)
   112  	seed, err := helpers.Seed(requestedState, epoch, params.BeaconConfig().DomainBeaconAttester)
   113  	if err != nil {
   114  		return nil, nil, status.Error(codes.Internal, "Could not get seed")
   115  	}
   116  	activeIndices, err := helpers.ActiveValidatorIndices(requestedState, epoch)
   117  	if err != nil {
   118  		return nil, nil, status.Error(codes.Internal, "Could not get active indices")
   119  	}
   120  
   121  	startSlot, err := helpers.StartSlot(epoch)
   122  	if err != nil {
   123  		return nil, nil, err
   124  	}
   125  	committeesListsBySlot, err := computeCommittees(startSlot, activeIndices, seed)
   126  	if err != nil {
   127  		return nil, nil, status.Errorf(
   128  			codes.InvalidArgument,
   129  			"Could not compute committees for epoch %d: %v",
   130  			epoch,
   131  			err,
   132  		)
   133  	}
   134  	return committeesListsBySlot, activeIndices, nil
   135  }
   136  
   137  // Compute committees given a start slot, active validator indices, and
   138  // the attester seeds value.
   139  func computeCommittees(
   140  	startSlot types.Slot,
   141  	activeIndices []types.ValidatorIndex,
   142  	attesterSeed [32]byte,
   143  ) (SlotToCommiteesMap, error) {
   144  	committeesListsBySlot := make(SlotToCommiteesMap, params.BeaconConfig().SlotsPerEpoch)
   145  	for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
   146  		var countAtSlot = uint64(len(activeIndices)) / uint64(params.BeaconConfig().SlotsPerEpoch) / params.BeaconConfig().TargetCommitteeSize
   147  		if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot {
   148  			countAtSlot = params.BeaconConfig().MaxCommitteesPerSlot
   149  		}
   150  		if countAtSlot == 0 {
   151  			countAtSlot = 1
   152  		}
   153  		committeeItems := make([]*ethpb.BeaconCommittees_CommitteeItem, countAtSlot)
   154  		for committeeIndex := uint64(0); committeeIndex < countAtSlot; committeeIndex++ {
   155  			committee, err := helpers.BeaconCommittee(activeIndices, attesterSeed, slot, types.CommitteeIndex(committeeIndex))
   156  			if err != nil {
   157  				return nil, status.Errorf(
   158  					codes.Internal,
   159  					"Could not compute committee for slot %d: %v",
   160  					slot,
   161  					err,
   162  				)
   163  			}
   164  			committeeItems[committeeIndex] = &ethpb.BeaconCommittees_CommitteeItem{
   165  				ValidatorIndices: committee,
   166  			}
   167  		}
   168  		committeesListsBySlot[slot] = &ethpb.BeaconCommittees_CommitteesList{
   169  			Committees: committeeItems,
   170  		}
   171  	}
   172  	return committeesListsBySlot, nil
   173  }
   174  
   175  // SlotToCommiteesMap represents <slot, CommitteeList> map.
   176  type SlotToCommiteesMap map[types.Slot]*ethpb.BeaconCommittees_CommitteesList
   177  
   178  // SlotToUint64 updates map keys to slots (workaround which will be unnecessary if we can cast
   179  // map<uint64, CommitteesList> correctly in beacon_chain.proto)
   180  func (m SlotToCommiteesMap) SlotToUint64() map[uint64]*ethpb.BeaconCommittees_CommitteesList {
   181  	updatedCommittees := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList, len(m))
   182  	for k, v := range m {
   183  		updatedCommittees[uint64(k)] = v
   184  	}
   185  	return updatedCommittees
   186  }