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 := &ethpb.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  }