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

     1  package validator
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  
     7  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
     8  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
     9  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    10  	"github.com/prysmaticlabs/prysm/shared/params"
    11  	"github.com/sirupsen/logrus"
    12  	"go.opencensus.io/trace"
    13  	"google.golang.org/grpc/codes"
    14  	"google.golang.org/grpc/status"
    15  )
    16  
    17  // SubmitAggregateSelectionProof is called by a validator when its assigned to be an aggregator.
    18  // The aggregator submits the selection proof to obtain the aggregated attestation
    19  // object to sign over.
    20  func (vs *Server) SubmitAggregateSelectionProof(ctx context.Context, req *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) {
    21  	ctx, span := trace.StartSpan(ctx, "AggregatorServer.SubmitAggregateSelectionProof")
    22  	defer span.End()
    23  	span.AddAttributes(trace.Int64Attribute("slot", int64(req.Slot)))
    24  
    25  	if vs.SyncChecker.Syncing() {
    26  		return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond")
    27  	}
    28  
    29  	st, err := vs.HeadFetcher.HeadState(ctx)
    30  	if err != nil {
    31  		return nil, status.Errorf(codes.Internal, "Could not determine head state: %v", err)
    32  	}
    33  
    34  	validatorIndex, exists := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(req.PublicKey))
    35  	if !exists {
    36  		return nil, status.Error(codes.Internal, "Could not locate validator index in DB")
    37  	}
    38  
    39  	epoch := helpers.SlotToEpoch(req.Slot)
    40  	activeValidatorIndices, err := helpers.ActiveValidatorIndices(st, epoch)
    41  	if err != nil {
    42  		return nil, status.Errorf(codes.Internal, "Could not get validators: %v", err)
    43  	}
    44  	seed, err := helpers.Seed(st, epoch, params.BeaconConfig().DomainBeaconAttester)
    45  	if err != nil {
    46  		return nil, status.Errorf(codes.Internal, "Could not get seed: %v", err)
    47  	}
    48  	committee, err := helpers.BeaconCommittee(activeValidatorIndices, seed, req.Slot, req.CommitteeIndex)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// Check if the validator is an aggregator
    54  	isAggregator, err := helpers.IsAggregator(uint64(len(committee)), req.SlotSignature)
    55  	if err != nil {
    56  		return nil, status.Errorf(codes.Internal, "Could not get aggregator status: %v", err)
    57  	}
    58  	if !isAggregator {
    59  		return nil, status.Errorf(codes.InvalidArgument, "Validator is not an aggregator")
    60  	}
    61  
    62  	if err := vs.AttPool.AggregateUnaggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex); err != nil {
    63  		return nil, status.Errorf(codes.Internal, "Could not aggregate unaggregated attestations")
    64  	}
    65  	aggregatedAtts := vs.AttPool.AggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex)
    66  
    67  	// Filter out the best aggregated attestation (ie. the one with the most aggregated bits).
    68  	if len(aggregatedAtts) == 0 {
    69  		aggregatedAtts = vs.AttPool.UnaggregatedAttestationsBySlotIndex(ctx, req.Slot, req.CommitteeIndex)
    70  		if len(aggregatedAtts) == 0 {
    71  			return nil, status.Errorf(codes.NotFound, "Could not find attestation for slot and committee in pool")
    72  		}
    73  	}
    74  
    75  	var indexInCommittee uint64
    76  	for i, idx := range committee {
    77  		if idx == validatorIndex {
    78  			indexInCommittee = uint64(i)
    79  		}
    80  	}
    81  
    82  	best := aggregatedAtts[0]
    83  	for _, aggregatedAtt := range aggregatedAtts[1:] {
    84  		// The aggregator should prefer an attestation that they have signed. We check this by
    85  		// looking at the attestation's committee index against the validator's committee index
    86  		// and check the aggregate bits to ensure the validator's index is set.
    87  		if aggregatedAtt.Data.CommitteeIndex == req.CommitteeIndex &&
    88  			aggregatedAtt.AggregationBits.BitAt(indexInCommittee) &&
    89  			(!best.AggregationBits.BitAt(indexInCommittee) ||
    90  				aggregatedAtt.AggregationBits.Count() > best.AggregationBits.Count()) {
    91  			best = aggregatedAtt
    92  		}
    93  
    94  		// If the "best" still doesn't contain the validator's index, check the aggregation bits to
    95  		// choose the attestation with the most bits set.
    96  		if !best.AggregationBits.BitAt(indexInCommittee) &&
    97  			aggregatedAtt.AggregationBits.Count() > best.AggregationBits.Count() {
    98  			best = aggregatedAtt
    99  		}
   100  	}
   101  	a := &ethpb.AggregateAttestationAndProof{
   102  		Aggregate:       best,
   103  		SelectionProof:  req.SlotSignature,
   104  		AggregatorIndex: validatorIndex,
   105  	}
   106  	return &ethpb.AggregateSelectionResponse{AggregateAndProof: a}, nil
   107  }
   108  
   109  // SubmitSignedAggregateSelectionProof is called by a validator to broadcast a signed
   110  // aggregated and proof object.
   111  func (vs *Server) SubmitSignedAggregateSelectionProof(
   112  	ctx context.Context,
   113  	req *ethpb.SignedAggregateSubmitRequest,
   114  ) (*ethpb.SignedAggregateSubmitResponse, error) {
   115  	if req.SignedAggregateAndProof == nil || req.SignedAggregateAndProof.Message == nil ||
   116  		req.SignedAggregateAndProof.Message.Aggregate == nil || req.SignedAggregateAndProof.Message.Aggregate.Data == nil {
   117  		return nil, status.Error(codes.InvalidArgument, "Signed aggregate request can't be nil")
   118  	}
   119  	emptySig := make([]byte, params.BeaconConfig().BLSSignatureLength)
   120  	if bytes.Equal(req.SignedAggregateAndProof.Signature, emptySig) ||
   121  		bytes.Equal(req.SignedAggregateAndProof.Message.SelectionProof, emptySig) {
   122  		return nil, status.Error(codes.InvalidArgument, "Signed signatures can't be zero hashes")
   123  	}
   124  
   125  	// As a preventive measure, a beacon node shouldn't broadcast an attestation whose slot is out of range.
   126  	if err := helpers.ValidateAttestationTime(req.SignedAggregateAndProof.Message.Aggregate.Data.Slot,
   127  		vs.TimeFetcher.GenesisTime(), params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
   128  		return nil, status.Error(codes.InvalidArgument, "Attestation slot is no longer valid from current time")
   129  	}
   130  
   131  	if err := vs.P2P.Broadcast(ctx, req.SignedAggregateAndProof); err != nil {
   132  		return nil, status.Errorf(codes.Internal, "Could not broadcast signed aggregated attestation: %v", err)
   133  	}
   134  
   135  	log.WithFields(logrus.Fields{
   136  		"slot":            req.SignedAggregateAndProof.Message.Aggregate.Data.Slot,
   137  		"committeeIndex":  req.SignedAggregateAndProof.Message.Aggregate.Data.CommitteeIndex,
   138  		"validatorIndex":  req.SignedAggregateAndProof.Message.AggregatorIndex,
   139  		"aggregatedCount": req.SignedAggregateAndProof.Message.Aggregate.AggregationBits.Count(),
   140  	}).Debug("Broadcasting aggregated attestation and proof")
   141  
   142  	return &ethpb.SignedAggregateSubmitResponse{}, nil
   143  }