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 }