github.com/prysmaticlabs/prysm@v1.4.4/validator/client/metrics.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/prometheus/client_golang/prometheus" 8 "github.com/prometheus/client_golang/prometheus/promauto" 9 types "github.com/prysmaticlabs/eth2-types" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 12 "github.com/prysmaticlabs/prysm/shared/bytesutil" 13 "github.com/prysmaticlabs/prysm/shared/params" 14 "github.com/sirupsen/logrus" 15 ) 16 17 var ( 18 // ValidatorStatusesGaugeVec used to track validator statuses by public key. 19 ValidatorStatusesGaugeVec = promauto.NewGaugeVec( 20 prometheus.GaugeOpts{ 21 Namespace: "validator", 22 Name: "statuses", 23 Help: "validator statuses: 0 UNKNOWN, 1 DEPOSITED, 2 PENDING, 3 ACTIVE, 4 EXITING, 5 SLASHING, 6 EXITED", 24 }, 25 []string{ 26 "pubkey", 27 }, 28 ) 29 // ValidatorAggSuccessVec used to count successful aggregations. 30 ValidatorAggSuccessVec = promauto.NewCounterVec( 31 prometheus.CounterOpts{ 32 Namespace: "validator", 33 Name: "successful_aggregations", 34 }, 35 []string{ 36 "pubkey", 37 }, 38 ) 39 // ValidatorAggFailVec used to count failed aggregations. 40 ValidatorAggFailVec = promauto.NewCounterVec( 41 prometheus.CounterOpts{ 42 Namespace: "validator", 43 Name: "failed_aggregations", 44 }, 45 []string{ 46 "pubkey", 47 }, 48 ) 49 // ValidatorProposeSuccessVec used to count successful proposals. 50 ValidatorProposeSuccessVec = promauto.NewCounterVec( 51 prometheus.CounterOpts{ 52 Namespace: "validator", 53 Name: "successful_proposals", 54 }, 55 []string{ 56 "pubkey", 57 }, 58 ) 59 // ValidatorProposeFailVec used to count failed proposals. 60 ValidatorProposeFailVec = promauto.NewCounterVec( 61 prometheus.CounterOpts{ 62 Namespace: "validator", 63 Name: "failed_proposals", 64 }, 65 []string{ 66 "pubkey", 67 }, 68 ) 69 // ValidatorProposeFailVecSlasher used to count failed proposals by slashing protection. 70 ValidatorProposeFailVecSlasher = promauto.NewCounterVec( 71 prometheus.CounterOpts{ 72 Name: "validator_proposals_rejected_total", 73 Help: "Count the block proposals rejected by slashing protection.", 74 }, 75 []string{ 76 "pubkey", 77 }, 78 ) 79 // ValidatorBalancesGaugeVec used to keep track of validator balances by public key. 80 ValidatorBalancesGaugeVec = promauto.NewGaugeVec( 81 prometheus.GaugeOpts{ 82 Namespace: "validator", 83 Name: "balance", 84 Help: "current validator balance.", 85 }, 86 []string{ 87 "pubkey", 88 }, 89 ) 90 // ValidatorInclusionDistancesGaugeVec used to keep track of validator inclusion distances by public key. 91 ValidatorInclusionDistancesGaugeVec = promauto.NewGaugeVec( 92 prometheus.GaugeOpts{ 93 Namespace: "validator", 94 Name: "inclusion_distance", 95 Help: "Inclusion distance of last attestation.", 96 }, 97 []string{ 98 "pubkey", 99 }, 100 ) 101 // ValidatorAttestedSlotsGaugeVec used to keep track of validator attested slots by public key. 102 ValidatorAttestedSlotsGaugeVec = promauto.NewGaugeVec( 103 prometheus.GaugeOpts{ 104 Namespace: "validator", 105 Name: "last_attested_slot", 106 Help: "Last attested slot.", 107 }, 108 []string{ 109 "pubkey", 110 }, 111 ) 112 // ValidatorCorrectlyVotedSourceGaugeVec used to keep track of validator's accuracy on voting source by public key. 113 ValidatorCorrectlyVotedSourceGaugeVec = promauto.NewGaugeVec( 114 prometheus.GaugeOpts{ 115 Namespace: "validator", 116 Name: "correctly_voted_source", 117 Help: "True if correctly voted source in last attestation.", 118 }, 119 []string{ 120 "pubkey", 121 }, 122 ) 123 // ValidatorCorrectlyVotedTargetGaugeVec used to keep track of validator's accuracy on voting target by public key. 124 ValidatorCorrectlyVotedTargetGaugeVec = promauto.NewGaugeVec( 125 prometheus.GaugeOpts{ 126 Namespace: "validator", 127 Name: "correctly_voted_target", 128 Help: "True if correctly voted target in last attestation.", 129 }, 130 []string{ 131 "pubkey", 132 }, 133 ) 134 // ValidatorCorrectlyVotedHeadGaugeVec used to keep track of validator's accuracy on voting head by public key. 135 ValidatorCorrectlyVotedHeadGaugeVec = promauto.NewGaugeVec( 136 prometheus.GaugeOpts{ 137 Namespace: "validator", 138 Name: "correctly_voted_head", 139 Help: "True if correctly voted head in last attestation.", 140 }, 141 []string{ 142 "pubkey", 143 }, 144 ) 145 // ValidatorAttestSuccessVec used to count successful attestations. 146 ValidatorAttestSuccessVec = promauto.NewCounterVec( 147 prometheus.CounterOpts{ 148 Namespace: "validator", 149 Name: "successful_attestations", 150 }, 151 []string{ 152 "pubkey", 153 }, 154 ) 155 // ValidatorAttestFailVec used to count failed attestations. 156 ValidatorAttestFailVec = promauto.NewCounterVec( 157 prometheus.CounterOpts{ 158 Namespace: "validator", 159 Name: "failed_attestations", 160 }, 161 []string{ 162 "pubkey", 163 }, 164 ) 165 // ValidatorAttestFailVecSlasher used to count failed attestations by slashing protection. 166 ValidatorAttestFailVecSlasher = promauto.NewCounterVec( 167 prometheus.CounterOpts{ 168 Name: "validator_attestations_rejected_total", 169 Help: "Count the attestations rejected by slashing protection.", 170 }, 171 []string{ 172 "pubkey", 173 }, 174 ) 175 // ValidatorNextAttestationSlotGaugeVec used to track validator statuses by public key. 176 ValidatorNextAttestationSlotGaugeVec = promauto.NewGaugeVec( 177 prometheus.GaugeOpts{ 178 Namespace: "validator", 179 Name: "next_attestation_slot", 180 Help: "validator next scheduled attestation slot", 181 }, 182 []string{ 183 "pubkey", 184 }, 185 ) 186 // ValidatorNextProposalSlotGaugeVec used to track validator statuses by public key. 187 ValidatorNextProposalSlotGaugeVec = promauto.NewGaugeVec( 188 prometheus.GaugeOpts{ 189 Namespace: "validator", 190 Name: "next_proposal_slot", 191 Help: "validator next scheduled proposal slot", 192 }, 193 []string{ 194 "pubkey", 195 }, 196 ) 197 ) 198 199 // LogValidatorGainsAndLosses logs important metrics related to this validator client's 200 // responsibilities throughout the beacon chain's lifecycle. It logs absolute accrued rewards 201 // and penalties over time, percentage gain/loss, and gives the end user a better idea 202 // of how the validator performs with respect to the rest. 203 func (v *validator) LogValidatorGainsAndLosses(ctx context.Context, slot types.Slot) error { 204 if !helpers.IsEpochEnd(slot) || slot <= params.BeaconConfig().SlotsPerEpoch { 205 // Do nothing unless we are at the end of the epoch, and not in the first epoch. 206 return nil 207 } 208 if !v.logValidatorBalances { 209 return nil 210 } 211 212 var pks [][48]byte 213 var err error 214 pks, err = v.keyManager.FetchValidatingPublicKeys(ctx) 215 if err != nil { 216 return err 217 } 218 pubKeys := bytesutil.FromBytes48Array(pks) 219 220 req := ðpb.ValidatorPerformanceRequest{ 221 PublicKeys: pubKeys, 222 } 223 resp, err := v.beaconClient.GetValidatorPerformance(ctx, req) 224 if err != nil { 225 return err 226 } 227 228 if v.emitAccountMetrics { 229 for _, missingPubKey := range resp.MissingValidators { 230 fmtKey := fmt.Sprintf("%#x", missingPubKey) 231 ValidatorBalancesGaugeVec.WithLabelValues(fmtKey).Set(0) 232 } 233 } 234 235 prevEpoch := types.Epoch(0) 236 if slot >= params.BeaconConfig().SlotsPerEpoch { 237 prevEpoch = types.Epoch(slot/params.BeaconConfig().SlotsPerEpoch) - 1 238 if uint64(v.voteStats.startEpoch) == ^uint64(0) { // Handles unknown first epoch. 239 v.voteStats.startEpoch = prevEpoch 240 } 241 } 242 gweiPerEth := float64(params.BeaconConfig().GweiPerEth) 243 v.prevBalanceLock.Lock() 244 for i, pubKey := range resp.PublicKeys { 245 pubKeyBytes := bytesutil.ToBytes48(pubKey) 246 if slot < params.BeaconConfig().SlotsPerEpoch { 247 v.prevBalance[pubKeyBytes] = params.BeaconConfig().MaxEffectiveBalance 248 } 249 if _, ok := v.startBalances[pubKeyBytes]; !ok { 250 v.startBalances[pubKeyBytes] = resp.BalancesBeforeEpochTransition[i] 251 } 252 253 fmtKey := fmt.Sprintf("%#x", pubKey) 254 truncatedKey := fmt.Sprintf("%#x", bytesutil.Trunc(pubKey)) 255 if v.prevBalance[pubKeyBytes] > 0 { 256 newBalance := float64(resp.BalancesAfterEpochTransition[i]) / gweiPerEth 257 prevBalance := float64(resp.BalancesBeforeEpochTransition[i]) / gweiPerEth 258 startBalance := float64(v.startBalances[pubKeyBytes]) / gweiPerEth 259 percentNet := (newBalance - prevBalance) / prevBalance 260 percentSinceStart := (newBalance - startBalance) / startBalance 261 log.WithFields(logrus.Fields{ 262 "pubKey": truncatedKey, 263 "epoch": prevEpoch, 264 "correctlyVotedSource": resp.CorrectlyVotedSource[i], 265 "correctlyVotedTarget": resp.CorrectlyVotedTarget[i], 266 "correctlyVotedHead": resp.CorrectlyVotedHead[i], 267 "inclusionSlot": resp.InclusionSlots[i], 268 "inclusionDistance": resp.InclusionDistances[i], 269 "startBalance": startBalance, 270 "oldBalance": prevBalance, 271 "newBalance": newBalance, 272 "percentChange": fmt.Sprintf("%.5f%%", percentNet*100), 273 "percentChangeSinceStart": fmt.Sprintf("%.5f%%", percentSinceStart*100), 274 }).Info("Previous epoch voting summary") 275 if v.emitAccountMetrics { 276 ValidatorBalancesGaugeVec.WithLabelValues(fmtKey).Set(newBalance) 277 ValidatorInclusionDistancesGaugeVec.WithLabelValues(fmtKey).Set(float64(resp.InclusionDistances[i])) 278 if resp.CorrectlyVotedSource[i] { 279 ValidatorCorrectlyVotedSourceGaugeVec.WithLabelValues(fmtKey).Set(1) 280 } else { 281 ValidatorCorrectlyVotedSourceGaugeVec.WithLabelValues(fmtKey).Set(0) 282 } 283 if resp.CorrectlyVotedTarget[i] { 284 ValidatorCorrectlyVotedTargetGaugeVec.WithLabelValues(fmtKey).Set(1) 285 } else { 286 ValidatorCorrectlyVotedTargetGaugeVec.WithLabelValues(fmtKey).Set(0) 287 } 288 if resp.CorrectlyVotedHead[i] { 289 ValidatorCorrectlyVotedHeadGaugeVec.WithLabelValues(fmtKey).Set(1) 290 } else { 291 ValidatorCorrectlyVotedHeadGaugeVec.WithLabelValues(fmtKey).Set(0) 292 } 293 294 } 295 } 296 v.prevBalance[pubKeyBytes] = resp.BalancesBeforeEpochTransition[i] 297 } 298 v.prevBalanceLock.Unlock() 299 300 v.UpdateLogAggregateStats(resp, slot) 301 return nil 302 } 303 304 // UpdateLogAggregateStats updates and logs the voteStats struct of a validator using the RPC response obtained from LogValidatorGainsAndLosses. 305 func (v *validator) UpdateLogAggregateStats(resp *ethpb.ValidatorPerformanceResponse, slot types.Slot) { 306 summary := &v.voteStats 307 currentEpoch := types.Epoch(slot / params.BeaconConfig().SlotsPerEpoch) 308 var included uint64 309 var correctSource, correctTarget, correctHead int 310 311 for i := range resp.PublicKeys { 312 if uint64(resp.InclusionSlots[i]) != ^uint64(0) { 313 included++ 314 summary.includedAttestedCount++ 315 summary.totalDistance += resp.InclusionDistances[i] 316 } 317 if resp.CorrectlyVotedSource[i] { 318 correctSource++ 319 summary.correctSources++ 320 } 321 if resp.CorrectlyVotedTarget[i] { 322 correctTarget++ 323 summary.correctTargets++ 324 } 325 if resp.CorrectlyVotedHead[i] { 326 correctHead++ 327 summary.correctHeads++ 328 } 329 } 330 331 // Return early if no attestation got included from previous epoch. 332 // This happens when validators joined half way through epoch and already passed its assigned slot. 333 if included == 0 { 334 return 335 } 336 337 summary.totalAttestedCount += uint64(len(resp.InclusionSlots)) 338 summary.totalSources += included 339 summary.totalTargets += included 340 summary.totalHeads += included 341 342 log.WithFields(logrus.Fields{ 343 "epoch": currentEpoch - 1, 344 "attestationInclusionPct": fmt.Sprintf("%.0f%%", (float64(included)/float64(len(resp.InclusionSlots)))*100), 345 "correctlyVotedSourcePct": fmt.Sprintf("%.0f%%", (float64(correctSource)/float64(included))*100), 346 "correctlyVotedTargetPct": fmt.Sprintf("%.0f%%", (float64(correctTarget)/float64(included))*100), 347 "correctlyVotedHeadPct": fmt.Sprintf("%.0f%%", (float64(correctHead)/float64(included))*100), 348 }).Info("Previous epoch aggregated voting summary") 349 350 var totalStartBal, totalPrevBal uint64 351 for i, val := range v.startBalances { 352 totalStartBal += val 353 totalPrevBal += v.prevBalance[i] 354 } 355 356 log.WithFields(logrus.Fields{ 357 "numberOfEpochs": fmt.Sprintf("%d", currentEpoch-summary.startEpoch), 358 "attestationsInclusionPct": fmt.Sprintf("%.0f%%", (float64(summary.includedAttestedCount)/float64(summary.totalAttestedCount))*100), 359 "averageInclusionDistance": fmt.Sprintf("%.2f slots", float64(summary.totalDistance)/float64(summary.includedAttestedCount)), 360 "correctlyVotedSourcePct": fmt.Sprintf("%.0f%%", (float64(summary.correctSources)/float64(summary.totalSources))*100), 361 "correctlyVotedTargetPct": fmt.Sprintf("%.0f%%", (float64(summary.correctTargets)/float64(summary.totalTargets))*100), 362 "correctlyVotedHeadPct": fmt.Sprintf("%.0f%%", (float64(summary.correctHeads)/float64(summary.totalHeads))*100), 363 "pctChangeCombinedBalance": fmt.Sprintf("%.5f%%", (float64(totalPrevBal)-float64(totalStartBal))/float64(totalStartBal)*100), 364 }).Info("Vote summary since launch") 365 }