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 = ðpb.AttestationData{ 132 Slot: req.Slot, 133 CommitteeIndex: req.CommitteeIndex, 134 BeaconBlockRoot: headRoot, 135 Source: headState.CurrentJustifiedCheckpoint(), 136 Target: ðpb.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 ðpb.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 }