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

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/prysmaticlabs/prysm/shared/copyutil"
     9  
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/cache"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    13  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
    14  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    16  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    17  	"github.com/prysmaticlabs/prysm/shared/bls"
    18  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    19  	"github.com/prysmaticlabs/prysm/shared/featureconfig"
    20  	"github.com/prysmaticlabs/prysm/shared/params"
    21  	"go.opencensus.io/trace"
    22  	"google.golang.org/grpc/codes"
    23  	"google.golang.org/grpc/status"
    24  	"google.golang.org/protobuf/types/known/emptypb"
    25  )
    26  
    27  // GetAttestationData requests that the beacon node produce an attestation data object,
    28  // which the validator acting as an attester will then sign.
    29  func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) {
    30  	ctx, span := trace.StartSpan(ctx, "AttesterServer.RequestAttestation")
    31  	defer span.End()
    32  	span.AddAttributes(
    33  		trace.Int64Attribute("slot", int64(req.Slot)),
    34  		trace.Int64Attribute("committeeIndex", int64(req.CommitteeIndex)),
    35  	)
    36  
    37  	if vs.SyncChecker.Syncing() {
    38  		return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond")
    39  	}
    40  
    41  	if err := helpers.ValidateAttestationTime(req.Slot, vs.TimeFetcher.GenesisTime(),
    42  		params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
    43  		return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid request: %v", err))
    44  	}
    45  
    46  	res, err := vs.AttestationCache.Get(ctx, req)
    47  	if err != nil {
    48  		return nil, status.Errorf(codes.Internal, "Could not retrieve data from attestation cache: %v", err)
    49  	}
    50  	if res != nil {
    51  		res.CommitteeIndex = req.CommitteeIndex
    52  		return res, nil
    53  	}
    54  
    55  	if err := vs.AttestationCache.MarkInProgress(req); err != nil {
    56  		if errors.Is(err, cache.ErrAlreadyInProgress) {
    57  			res, err := vs.AttestationCache.Get(ctx, req)
    58  			if err != nil {
    59  				return nil, status.Errorf(codes.Internal, "Could not retrieve data from attestation cache: %v", err)
    60  			}
    61  			if res == nil {
    62  				return nil, status.Error(codes.DataLoss, "A request was in progress and resolved to nil")
    63  			}
    64  			res.CommitteeIndex = req.CommitteeIndex
    65  			return res, nil
    66  		}
    67  		return nil, status.Errorf(codes.Internal, "Could not mark attestation as in-progress: %v", err)
    68  	}
    69  	defer func() {
    70  		if err := vs.AttestationCache.MarkNotInProgress(req); err != nil {
    71  			log.WithError(err).Error("Could not mark cache not in progress")
    72  		}
    73  	}()
    74  
    75  	headState, err := vs.HeadFetcher.HeadState(ctx)
    76  	if err != nil {
    77  		return nil, status.Errorf(codes.Internal, "Could not retrieve head state: %v", err)
    78  	}
    79  	headRoot, err := vs.HeadFetcher.HeadRoot(ctx)
    80  	if err != nil {
    81  		return nil, status.Errorf(codes.Internal, "Could not retrieve head root: %v", err)
    82  	}
    83  
    84  	// In the case that we receive an attestation request after a newer state/block has been processed.
    85  	if headState.Slot() > req.Slot {
    86  		headRoot, err = helpers.BlockRootAtSlot(headState, req.Slot)
    87  		if err != nil {
    88  			return nil, status.Errorf(codes.Internal, "Could not get historical head root: %v", err)
    89  		}
    90  		headState, err = vs.StateGen.StateByRoot(ctx, bytesutil.ToBytes32(headRoot))
    91  		if err != nil {
    92  			return nil, status.Errorf(codes.Internal, "Could not get historical head state: %v", err)
    93  		}
    94  	}
    95  	if headState == nil || headState.IsNil() {
    96  		return nil, status.Error(codes.Internal, "Could not lookup parent state from head.")
    97  	}
    98  
    99  	if helpers.CurrentEpoch(headState) < helpers.SlotToEpoch(req.Slot) {
   100  		if featureconfig.Get().EnableNextSlotStateCache {
   101  			headState, err = state.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, req.Slot)
   102  			if err != nil {
   103  				return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err)
   104  			}
   105  		} else {
   106  			headState, err = state.ProcessSlots(ctx, headState, req.Slot)
   107  			if err != nil {
   108  				return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err)
   109  			}
   110  		}
   111  	}
   112  
   113  	targetEpoch := helpers.CurrentEpoch(headState)
   114  	epochStartSlot, err := helpers.StartSlot(targetEpoch)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	var targetRoot []byte
   119  	if epochStartSlot == headState.Slot() {
   120  		targetRoot = headRoot
   121  	} else {
   122  		targetRoot, err = helpers.BlockRootAtSlot(headState, epochStartSlot)
   123  		if err != nil {
   124  			return nil, status.Errorf(codes.Internal, "Could not get target block for slot %d: %v", epochStartSlot, err)
   125  		}
   126  		if bytesutil.ToBytes32(targetRoot) == params.BeaconConfig().ZeroHash {
   127  			targetRoot = headRoot
   128  		}
   129  	}
   130  
   131  	res = &ethpb.AttestationData{
   132  		Slot:            req.Slot,
   133  		CommitteeIndex:  req.CommitteeIndex,
   134  		BeaconBlockRoot: headRoot,
   135  		Source:          headState.CurrentJustifiedCheckpoint(),
   136  		Target: &ethpb.Checkpoint{
   137  			Epoch: targetEpoch,
   138  			Root:  targetRoot,
   139  		},
   140  	}
   141  
   142  	if err := vs.AttestationCache.Put(ctx, req, res); err != nil {
   143  		return nil, status.Errorf(codes.Internal, "Could not store attestation data in cache: %v", err)
   144  	}
   145  	return res, nil
   146  }
   147  
   148  // ProposeAttestation is a function called by an attester to vote
   149  // on a block via an attestation object as defined in the Ethereum Serenity specification.
   150  func (vs *Server) ProposeAttestation(ctx context.Context, att *ethpb.Attestation) (*ethpb.AttestResponse, error) {
   151  	ctx, span := trace.StartSpan(ctx, "AttesterServer.ProposeAttestation")
   152  	defer span.End()
   153  
   154  	if _, err := bls.SignatureFromBytes(att.Signature); err != nil {
   155  		return nil, status.Error(codes.InvalidArgument, "Incorrect attestation signature")
   156  	}
   157  
   158  	root, err := att.Data.HashTreeRoot()
   159  	if err != nil {
   160  		return nil, status.Errorf(codes.Internal, "Could not tree hash attestation: %v", err)
   161  	}
   162  
   163  	// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
   164  	// of a received unaggregated attestation.
   165  	vs.OperationNotifier.OperationFeed().Send(&feed.Event{
   166  		Type: operation.UnaggregatedAttReceived,
   167  		Data: &operation.UnAggregatedAttReceivedData{
   168  			Attestation: att,
   169  		},
   170  	})
   171  
   172  	// Determine subnet to broadcast attestation to
   173  	wantedEpoch := helpers.SlotToEpoch(att.Data.Slot)
   174  	vals, err := vs.HeadFetcher.HeadValidatorsIndices(ctx, wantedEpoch)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	subnet := helpers.ComputeSubnetFromCommitteeAndSlot(uint64(len(vals)), att.Data.CommitteeIndex, att.Data.Slot)
   179  
   180  	// Broadcast the new attestation to the network.
   181  	if err := vs.P2P.BroadcastAttestation(ctx, subnet, att); err != nil {
   182  		return nil, status.Errorf(codes.Internal, "Could not broadcast attestation: %v", err)
   183  	}
   184  
   185  	go func() {
   186  		ctx = trace.NewContext(context.Background(), trace.FromContext(ctx))
   187  		attCopy := copyutil.CopyAttestation(att)
   188  		if err := vs.AttPool.SaveUnaggregatedAttestation(attCopy); err != nil {
   189  			log.WithError(err).Error("Could not handle attestation in operations service")
   190  			return
   191  		}
   192  	}()
   193  
   194  	return &ethpb.AttestResponse{
   195  		AttestationDataRoot: root[:],
   196  	}, nil
   197  }
   198  
   199  // SubscribeCommitteeSubnets subscribes to the committee ID subnet given subscribe request.
   200  func (vs *Server) SubscribeCommitteeSubnets(ctx context.Context, req *ethpb.CommitteeSubnetsSubscribeRequest) (*emptypb.Empty, error) {
   201  	ctx, span := trace.StartSpan(ctx, "AttesterServer.SubscribeCommitteeSubnets")
   202  	defer span.End()
   203  
   204  	if len(req.Slots) != len(req.CommitteeIds) || len(req.CommitteeIds) != len(req.IsAggregator) {
   205  		return nil, status.Error(codes.InvalidArgument, "request fields are not the same length")
   206  	}
   207  	if len(req.Slots) == 0 {
   208  		return nil, status.Error(codes.InvalidArgument, "no attester slots provided")
   209  	}
   210  
   211  	fetchValsLen := func(slot types.Slot) (uint64, error) {
   212  		wantedEpoch := helpers.SlotToEpoch(slot)
   213  		vals, err := vs.HeadFetcher.HeadValidatorsIndices(ctx, wantedEpoch)
   214  		if err != nil {
   215  			return 0, err
   216  		}
   217  		return uint64(len(vals)), nil
   218  	}
   219  
   220  	// Request the head validator indices of epoch represented by the first requested
   221  	// slot.
   222  	currValsLen, err := fetchValsLen(req.Slots[0])
   223  	if err != nil {
   224  		return nil, status.Errorf(codes.Internal, "Could not retrieve head validator length: %v", err)
   225  	}
   226  	currEpoch := helpers.SlotToEpoch(req.Slots[0])
   227  
   228  	for i := 0; i < len(req.Slots); i++ {
   229  		// If epoch has changed, re-request active validators length
   230  		if currEpoch != helpers.SlotToEpoch(req.Slots[i]) {
   231  			currValsLen, err = fetchValsLen(req.Slots[i])
   232  			if err != nil {
   233  				return nil, status.Errorf(codes.Internal, "Could not retrieve head validator length: %v", err)
   234  			}
   235  			currEpoch = helpers.SlotToEpoch(req.Slots[i])
   236  		}
   237  		subnet := helpers.ComputeSubnetFromCommitteeAndSlot(currValsLen, req.CommitteeIds[i], req.Slots[i])
   238  		cache.SubnetIDs.AddAttesterSubnetID(req.Slots[i], subnet)
   239  		if req.IsAggregator[i] {
   240  			cache.SubnetIDs.AddAggregatorSubnetID(req.Slots[i], subnet)
   241  		}
   242  	}
   243  
   244  	return &emptypb.Empty{}, nil
   245  }