github.com/prysmaticlabs/prysm@v1.4.4/validator/client/aggregate.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2" 12 "github.com/prysmaticlabs/prysm/shared/bls" 13 "github.com/prysmaticlabs/prysm/shared/params" 14 "github.com/prysmaticlabs/prysm/shared/slotutil" 15 "github.com/prysmaticlabs/prysm/shared/timeutils" 16 "github.com/prysmaticlabs/prysm/shared/traceutil" 17 "go.opencensus.io/trace" 18 "google.golang.org/grpc/codes" 19 "google.golang.org/grpc/status" 20 ) 21 22 // SubmitAggregateAndProof submits the validator's signed slot signature to the beacon node 23 // via gRPC. Beacon node will verify the slot signature and determine if the validator is also 24 // an aggregator. If yes, then beacon node will broadcast aggregated signature and 25 // proof on the validator's behalf. 26 func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot types.Slot, pubKey [48]byte) { 27 ctx, span := trace.StartSpan(ctx, "validator.SubmitAggregateAndProof") 28 defer span.End() 29 30 span.AddAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey))) 31 fmtKey := fmt.Sprintf("%#x", pubKey[:]) 32 33 duty, err := v.duty(pubKey) 34 if err != nil { 35 log.Errorf("Could not fetch validator assignment: %v", err) 36 if v.emitAccountMetrics { 37 ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() 38 } 39 return 40 } 41 42 // Avoid sending beacon node duplicated aggregation requests. 43 k := validatorSubscribeKey(slot, duty.CommitteeIndex) 44 v.aggregatedSlotCommitteeIDCacheLock.Lock() 45 if v.aggregatedSlotCommitteeIDCache.Contains(k) { 46 v.aggregatedSlotCommitteeIDCacheLock.Unlock() 47 return 48 } 49 v.aggregatedSlotCommitteeIDCache.Add(k, true) 50 v.aggregatedSlotCommitteeIDCacheLock.Unlock() 51 52 slotSig, err := v.signSlotWithSelectionProof(ctx, pubKey, slot) 53 if err != nil { 54 log.Errorf("Could not sign slot: %v", err) 55 if v.emitAccountMetrics { 56 ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() 57 } 58 return 59 } 60 61 // As specified in spec, an aggregator should wait until two thirds of the way through slot 62 // to broadcast the best aggregate to the global aggregate channel. 63 // https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/validator/0_beacon-chain-validator.md#broadcast-aggregate 64 v.waitToSlotTwoThirds(ctx, slot) 65 66 res, err := v.validatorClient.SubmitAggregateSelectionProof(ctx, ðpb.AggregateSelectionRequest{ 67 Slot: slot, 68 CommitteeIndex: duty.CommitteeIndex, 69 PublicKey: pubKey[:], 70 SlotSignature: slotSig, 71 }) 72 if err != nil { 73 s, ok := status.FromError(err) 74 if ok && s.Code() == codes.NotFound { 75 log.WithField("slot", slot).WithError(err).Warn("No attestations to aggregate") 76 } else { 77 log.WithField("slot", slot).WithError(err).Error("Could not submit slot signature to beacon node") 78 if v.emitAccountMetrics { 79 ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() 80 } 81 } 82 83 return 84 } 85 86 sig, err := v.aggregateAndProofSig(ctx, pubKey, res.AggregateAndProof) 87 if err != nil { 88 log.Errorf("Could not sign aggregate and proof: %v", err) 89 return 90 } 91 _, err = v.validatorClient.SubmitSignedAggregateSelectionProof(ctx, ðpb.SignedAggregateSubmitRequest{ 92 SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProof{ 93 Message: res.AggregateAndProof, 94 Signature: sig, 95 }, 96 }) 97 if err != nil { 98 log.Errorf("Could not submit signed aggregate and proof to beacon node: %v", err) 99 if v.emitAccountMetrics { 100 ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() 101 } 102 return 103 } 104 105 if err := v.addIndicesToLog(duty); err != nil { 106 log.Errorf("Could not add aggregator indices to logs: %v", err) 107 if v.emitAccountMetrics { 108 ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() 109 } 110 return 111 } 112 if v.emitAccountMetrics { 113 ValidatorAggSuccessVec.WithLabelValues(fmtKey).Inc() 114 } 115 116 } 117 118 // Signs input slot with domain selection proof. This is used to create the signature for aggregator selection. 119 func (v *validator) signSlotWithSelectionProof(ctx context.Context, pubKey [48]byte, slot types.Slot) (signature []byte, error error) { 120 domain, err := v.domainData(ctx, helpers.SlotToEpoch(slot), params.BeaconConfig().DomainSelectionProof[:]) 121 if err != nil { 122 return nil, err 123 } 124 125 var sig bls.Signature 126 sszUint := types.SSZUint64(slot) 127 root, err := helpers.ComputeSigningRoot(&sszUint, domain.SignatureDomain) 128 if err != nil { 129 return nil, err 130 } 131 sig, err = v.keyManager.Sign(ctx, &validatorpb.SignRequest{ 132 PublicKey: pubKey[:], 133 SigningRoot: root[:], 134 SignatureDomain: domain.SignatureDomain, 135 Object: &validatorpb.SignRequest_Slot{Slot: slot}, 136 }) 137 if err != nil { 138 return nil, err 139 } 140 141 return sig.Marshal(), nil 142 } 143 144 // waitToSlotTwoThirds waits until two third through the current slot period 145 // such that any attestations from this slot have time to reach the beacon node 146 // before creating the aggregated attestation. 147 func (v *validator) waitToSlotTwoThirds(ctx context.Context, slot types.Slot) { 148 ctx, span := trace.StartSpan(ctx, "validator.waitToSlotTwoThirds") 149 defer span.End() 150 151 oneThird := slotutil.DivideSlotBy(3 /* one third of slot duration */) 152 twoThird := oneThird + oneThird 153 delay := twoThird 154 155 startTime := slotutil.SlotStartTime(v.genesisTime, slot) 156 finalTime := startTime.Add(delay) 157 wait := timeutils.Until(finalTime) 158 if wait <= 0 { 159 return 160 } 161 t := time.NewTimer(wait) 162 defer t.Stop() 163 select { 164 case <-ctx.Done(): 165 traceutil.AnnotateError(span, ctx.Err()) 166 return 167 case <-t.C: 168 return 169 } 170 } 171 172 // This returns the signature of validator signing over aggregate and 173 // proof object. 174 func (v *validator) aggregateAndProofSig(ctx context.Context, pubKey [48]byte, agg *ethpb.AggregateAttestationAndProof) ([]byte, error) { 175 d, err := v.domainData(ctx, helpers.SlotToEpoch(agg.Aggregate.Data.Slot), params.BeaconConfig().DomainAggregateAndProof[:]) 176 if err != nil { 177 return nil, err 178 } 179 var sig bls.Signature 180 root, err := helpers.ComputeSigningRoot(agg, d.SignatureDomain) 181 if err != nil { 182 return nil, err 183 } 184 sig, err = v.keyManager.Sign(ctx, &validatorpb.SignRequest{ 185 PublicKey: pubKey[:], 186 SigningRoot: root[:], 187 SignatureDomain: d.SignatureDomain, 188 Object: &validatorpb.SignRequest_AggregateAttestationAndProof{AggregateAttestationAndProof: agg}, 189 }) 190 if err != nil { 191 return nil, err 192 } 193 194 return sig.Marshal(), nil 195 } 196 197 func (v *validator) addIndicesToLog(duty *ethpb.DutiesResponse_Duty) error { 198 v.attLogsLock.Lock() 199 defer v.attLogsLock.Unlock() 200 201 for _, log := range v.attLogs { 202 if duty.CommitteeIndex == log.data.CommitteeIndex { 203 log.aggregatorIndices = append(log.aggregatorIndices, duty.ValidatorIndex) 204 } 205 } 206 207 return nil 208 }