code.vegaprotocol.io/vega@v0.79.0/core/validators/validator_performance.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package validators
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"encoding/hex"
    22  	"strings"
    23  
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	"code.vegaprotocol.io/vega/logging"
    26  
    27  	"github.com/cometbft/cometbft/crypto/sr25519"
    28  )
    29  
    30  var (
    31  	decimalOne         = num.DecimalFromFloat(1)
    32  	minPerfScore       = num.DecimalFromFloat(0.05)
    33  	minBlocksTolerance = num.DecimalFromFloat(2)
    34  )
    35  
    36  type validatorPerformance struct {
    37  	proposals map[string]int64
    38  	total     int64
    39  	log       *logging.Logger
    40  }
    41  
    42  func NewValidatorPerformance(log *logging.Logger) *validatorPerformance { //revive:disable:unexported-return
    43  	return &validatorPerformance{
    44  		proposals: map[string]int64{},
    45  		total:     0,
    46  		log:       log,
    47  	}
    48  }
    49  
    50  func tmPubKeyToAddress(tmPubKey string) string {
    51  	if len(tmPubKey) == 0 {
    52  		return ""
    53  	}
    54  	pubkey, err := base64.StdEncoding.DecodeString(tmPubKey)
    55  	if err != nil {
    56  		return ""
    57  	}
    58  	pke := sr25519.PubKey(pubkey)
    59  	address := hex.EncodeToString(pke.Address().Bytes())
    60  	return strings.ToLower(address)
    61  }
    62  
    63  // ValidatorPerformanceScore returns the validator's performance score calculated as the numer of proposals out of the total number of proposals
    64  // normalised by their power out of the total power.
    65  func (vp *validatorPerformance) ValidatorPerformanceScore(tmPubKey string, power, totalPower int64, performanceScalingFactor num.Decimal) num.Decimal {
    66  	if vp.total == 0 || totalPower == 0 {
    67  		return minPerfScore
    68  	}
    69  
    70  	// convert from tendermint public key key to address
    71  	address := tmPubKeyToAddress(tmPubKey)
    72  	noProposals := int64(0)
    73  	if _, ok := vp.proposals[address]; ok {
    74  		noProposals = vp.proposals[address]
    75  	}
    76  
    77  	// the actual number of blocks proposed is scaled by the maximum of the hardcoded <minBlocksTolerance> and
    78  	// the network parameter performanceScalingFactor
    79  	noProposalsD := num.DecimalFromInt64(noProposals).Add(num.MaxD(minBlocksTolerance, num.DecimalFromInt64(noProposals).Mul(performanceScalingFactor)))
    80  	actual := noProposalsD.Div(num.DecimalFromInt64(vp.total))
    81  	expected := num.DecimalFromInt64(power).Div(num.DecimalFromInt64(totalPower))
    82  	score := num.MaxD(minPerfScore, num.MinD(decimalOne, actual.Div(expected)))
    83  	vp.log.Info("looking up performance for", logging.String("address", address), logging.String("perf-score", score.String()))
    84  	return score
    85  }
    86  
    87  // BeginBlock is called when a new block begins. it calculates who should have been the proposer and updates the counters with the expected and actual proposers and voters.
    88  func (vp *validatorPerformance) BeginBlock(ctx context.Context, proposer string) {
    89  	if _, ok := vp.proposals[proposer]; !ok {
    90  		vp.proposals[proposer] = 0
    91  	}
    92  	vp.proposals[proposer]++
    93  	vp.total++
    94  }
    95  
    96  func (vp *validatorPerformance) Reset() {
    97  	vp.total = 0
    98  	vp.proposals = map[string]int64{}
    99  }