code.vegaprotocol.io/vega@v0.79.0/core/validators/validator_score_test.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 "math/rand" 21 "testing" 22 23 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/libs/num" 27 "code.vegaprotocol.io/vega/logging" 28 v1 "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 "golang.org/x/exp/maps" 33 ) 34 35 type TestMultisigTopology struct { 36 validators map[string]struct{} 37 } 38 39 func (t *TestMultisigTopology) ChainID() string { 40 return "12" 41 } 42 43 func (t *TestMultisigTopology) IsSigner(address string) bool { 44 _, ok := t.validators[address] 45 return ok 46 } 47 48 func (t *TestMultisigTopology) ExcessSigners(addresses []string) bool { 49 if len(t.validators) > len(addresses) { 50 return true 51 } 52 53 m := make(map[string]struct{}, len(addresses)) 54 for _, v := range addresses { 55 m[v] = struct{}{} 56 } 57 58 for k := range t.validators { 59 if _, ok := m[k]; !ok { 60 return true 61 } 62 } 63 return false 64 } 65 66 func (t *TestMultisigTopology) GetSigners() []string { 67 signers := make([]string, 0, len(t.validators)) 68 for k := range t.validators { 69 signers = append(signers, k) 70 } 71 return signers 72 } 73 74 func (t *TestMultisigTopology) GetThreshold() uint32 { 75 return 666 76 } 77 78 func TestScores(t *testing.T) { 79 t.Run("test calculation of stake score with no anti-whaling", testStakeScore) 80 t.Run("test calculation of performance score", testPerformanceScore) 81 t.Run("test calculation of ranking score from stake and performance scores", testRankingScoreInternal) 82 t.Run("test calculation of ranking score from delegation data and validators state", testRankingScore) 83 t.Run("test score normalisation", testNormalisedScores) 84 t.Run("test validator score with anti whaling", testValidatorScore) 85 t.Run("test composition of raw validator score for rewards with performance score", testGetValScore) 86 t.Run("test multisig score", testGetMultisigScore) 87 t.Run("test multisig score more validators than signers", TestGetMultisigScoreMoreValidatorsThanSigners) 88 t.Run("test calculate tm rewards scores", testCalculateTMScores) 89 t.Run("test calculate ersatz rewards scores", testCalculateErsatzScores) 90 t.Run("test calculate rewards scores", testGetRewardsScores) 91 } 92 93 func testStakeScore(t *testing.T) { 94 validatorData := []*types.ValidatorData{ 95 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 96 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 97 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 98 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 99 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 100 } 101 102 // node1 has 10000 / 60000 = 0.1666666667 103 // node2 has 5000 / 60000 = 0.08333333333 104 // node3 has 10000 / 60000 = 0.1666666667 105 // node4 has 15000 / 60000 = 0.25 106 // node5 has 20000 / 60000 = 0.3333333333 107 scores := getStakeScore(validatorData) 108 require.Equal(t, "0.1666666666666667", scores["node1"].String()) 109 require.Equal(t, "0.0833333333333333", scores["node2"].String()) 110 require.Equal(t, "0.1666666666666667", scores["node3"].String()) 111 require.Equal(t, "0.25", scores["node4"].String()) 112 require.Equal(t, "0.3333333333333333", scores["node5"].String()) 113 } 114 115 func testPerformanceScore(t *testing.T) { 116 topology := &Topology{} 117 topology.log = logging.NewTestLogger() 118 topology.validators = map[string]*valState{} 119 delegation := []*types.ValidatorData{ 120 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 121 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 122 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 123 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 124 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 125 } 126 127 // node1 has less than the minimum self stake => 0 128 // node2 is a tendermint validator and gets a performance score of 0.8 129 // node3 is pending and hasn't forwarded yet 3 events 130 // node4 is pending and hasn't voted yet for 3 events 131 // node5 is in the waiting list and has signed 9 of the 10 expected messages 132 133 topology.validators["node1"] = &valState{ 134 data: ValidatorData{ 135 ID: "node1", 136 }, 137 heartbeatTracker: &validatorHeartbeatTracker{}, 138 } 139 topology.validators["node2"] = &valState{ 140 data: ValidatorData{ 141 ID: "node2", 142 }, 143 status: ValidatorStatusTendermint, 144 heartbeatTracker: &validatorHeartbeatTracker{}, 145 } 146 topology.validators["node3"] = &valState{ 147 data: ValidatorData{ 148 ID: "node3", 149 }, 150 status: ValidatorStatusPending, 151 numberOfEthereumEventsForwarded: 2, 152 heartbeatTracker: &validatorHeartbeatTracker{}, 153 } 154 topology.validators["node4"] = &valState{ 155 data: ValidatorData{ 156 ID: "node4", 157 }, 158 status: ValidatorStatusPending, 159 numberOfEthereumEventsForwarded: 4, 160 heartbeatTracker: &validatorHeartbeatTracker{}, 161 } 162 topology.validators["node5"] = &valState{ 163 data: ValidatorData{ 164 ID: "node5", 165 }, 166 status: ValidatorStatusPending, 167 numberOfEthereumEventsForwarded: 4, 168 heartbeatTracker: &validatorHeartbeatTracker{ 169 blockSigs: [10]bool{false, true, true, true, true, true, true, true, true, true}, 170 }, 171 } 172 173 topology.minimumStake = num.NewUint(3000) 174 topology.validatorPerformance = &MockPerformanceScore{} 175 176 scores := topology.getPerformanceScore(delegation) 177 178 require.Equal(t, "0", scores["node1"].String()) 179 require.Equal(t, "0.8", scores["node2"].String()) 180 require.Equal(t, "0", scores["node3"].String()) 181 require.Equal(t, "0", scores["node4"].String()) 182 require.Equal(t, "0.9", scores["node5"].String()) 183 } 184 185 func testRankingScoreInternal(t *testing.T) { 186 stakeScores := map[string]num.Decimal{ 187 "node1": num.DecimalFromFloat(0.1), 188 "node2": num.DecimalFromFloat(0.15), 189 "node3": num.DecimalFromFloat(0.2), 190 "node4": num.DecimalFromFloat(0.25), 191 "node5": num.DecimalFromFloat(0.3), 192 } 193 perfScores := map[string]num.Decimal{ 194 "node1": num.DecimalZero(), 195 "node2": num.DecimalFromFloat(0.5), 196 "node3": num.DecimalFromFloat(0.9), 197 "node4": num.DecimalFromFloat(0.2), 198 "node5": num.DecimalFromFloat(1), 199 } 200 201 topology := &Topology{} 202 topology.log = logging.NewTestLogger() 203 topology.validators = map[string]*valState{} 204 topology.validators["node1"] = &valState{ 205 data: ValidatorData{ 206 ID: "node1", 207 }, 208 status: ValidatorStatusPending, 209 heartbeatTracker: &validatorHeartbeatTracker{}, 210 } 211 topology.validators["node2"] = &valState{ 212 data: ValidatorData{ 213 ID: "node2", 214 }, 215 status: ValidatorStatusTendermint, 216 heartbeatTracker: &validatorHeartbeatTracker{}, 217 } 218 topology.validators["node3"] = &valState{ 219 data: ValidatorData{ 220 ID: "node3", 221 }, 222 status: ValidatorStatusPending, 223 heartbeatTracker: &validatorHeartbeatTracker{}, 224 } 225 topology.validators["node4"] = &valState{ 226 data: ValidatorData{ 227 ID: "node4", 228 }, 229 status: ValidatorStatusErsatz, 230 heartbeatTracker: &validatorHeartbeatTracker{}, 231 } 232 topology.validators["node5"] = &valState{ 233 data: ValidatorData{ 234 ID: "node5", 235 }, 236 status: ValidatorStatusErsatz, 237 heartbeatTracker: &validatorHeartbeatTracker{}, 238 } 239 240 topology.validatorIncumbentBonusFactor = num.DecimalFromFloat(1.1) 241 rankingScores := topology.getRankingScoreInternal(stakeScores, perfScores) 242 243 // 0.1 * 0 = 0 244 require.Equal(t, "0", rankingScores["node1"].String()) 245 // 0.15 * 0.5 * 1.1 = 0.0825 246 require.Equal(t, "0.0825", rankingScores["node2"].String()) 247 // 0.2 * 0.9 = 0.18 248 require.Equal(t, "0.18", rankingScores["node3"].String()) 249 // 0.25 * 0.2 * 1.1 = 0.055 250 require.Equal(t, "0.055", rankingScores["node4"].String()) 251 // 0.3 * 1 * 1.1 = 0.33 252 require.Equal(t, "0.33", rankingScores["node5"].String()) 253 } 254 255 func testRankingScore(t *testing.T) { 256 topology := &Topology{} 257 topology.log = logging.NewTestLogger() 258 topology.validators = map[string]*valState{} 259 delegation := []*types.ValidatorData{ 260 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 261 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 262 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 263 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 264 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 265 } 266 267 // node1 has less than the minimum self stake => 0 268 // node2 is a tendermint validator and gets a performance score of 0.8 269 // node3 is pending and hasn't forwarded yet 3 events 270 // node4 is pending and hasn't voted yet for 3 events 271 // node5 is in the waiting list and has signed 9 of the 10 expected messages 272 273 topology.validators["node1"] = &valState{ 274 data: ValidatorData{ 275 ID: "node1", 276 }, 277 status: ValidatorStatusPending, 278 heartbeatTracker: &validatorHeartbeatTracker{}, 279 } 280 topology.validators["node2"] = &valState{ 281 data: ValidatorData{ 282 ID: "node2", 283 }, 284 status: ValidatorStatusTendermint, 285 heartbeatTracker: &validatorHeartbeatTracker{}, 286 } 287 topology.validators["node3"] = &valState{ 288 data: ValidatorData{ 289 ID: "node3", 290 }, 291 status: ValidatorStatusErsatz, 292 numberOfEthereumEventsForwarded: 4, 293 heartbeatTracker: &validatorHeartbeatTracker{ 294 blockSigs: [10]bool{false, false, true, false, true, false, true, true, true, true}, 295 }, 296 } 297 topology.validators["node4"] = &valState{ 298 data: ValidatorData{ 299 ID: "node4", 300 }, 301 status: ValidatorStatusPending, 302 numberOfEthereumEventsForwarded: 4, 303 heartbeatTracker: &validatorHeartbeatTracker{}, 304 } 305 topology.validators["node5"] = &valState{ 306 data: ValidatorData{ 307 ID: "node5", 308 }, 309 status: ValidatorStatusTendermint, 310 heartbeatTracker: &validatorHeartbeatTracker{}, 311 } 312 313 topology.minimumStake = num.NewUint(3000) 314 topology.validatorPerformance = &MockPerformanceScore{} 315 topology.validatorIncumbentBonusFactor = num.DecimalFromFloat(1.1) 316 317 stakeScores, perfScores, rankingScores := topology.getRankingScore(delegation) 318 require.Equal(t, "0.1666666666666667", stakeScores["node1"].String()) 319 require.Equal(t, "0.0833333333333333", stakeScores["node2"].String()) 320 require.Equal(t, "0.1666666666666667", stakeScores["node3"].String()) 321 require.Equal(t, "0.25", stakeScores["node4"].String()) 322 require.Equal(t, "0.3333333333333333", stakeScores["node5"].String()) 323 324 // less than min self stake 325 require.Equal(t, "0", perfScores["node1"].String()) 326 // tm validator performance = 0.8 327 require.Equal(t, "0.8", perfScores["node2"].String()) 328 // ersatz signed 6/10 => performance = 0.6 329 require.Equal(t, "0.6", perfScores["node3"].String()) 330 // pending - min requirements not met yet 331 require.Equal(t, "0", perfScores["node4"].String()) 332 // tm validator performance = 0.8 333 require.Equal(t, "0.8", perfScores["node5"].String()) 334 335 // ranking scores: 336 // 0.1666666666666667 * 0 = 0 337 require.Equal(t, "0", rankingScores["node1"].String()) 338 // 0.0833333333333333*0.8*1.1 = 0.07333333333 339 require.Equal(t, "0.073333333333333304", rankingScores["node2"].String()) 340 // 0.1666666666666667 * 0.6 * 1.1 = 0.11 341 require.Equal(t, "0.110000000000000022", rankingScores["node3"].String()) 342 // 0.25 * 0 = 0 343 require.Equal(t, "0", rankingScores["node4"].String()) 344 // 0.3333333333333333 * 1.1 * 0.8 = 0.2933333333 345 require.Equal(t, "0.293333333333333304", rankingScores["node5"].String()) 346 } 347 348 func testNormalisedScores(t *testing.T) { 349 rnd := rand.New(rand.NewSource(100000)) 350 scores := map[string]num.Decimal{ 351 "node1": num.DecimalZero(), 352 "node2": num.DecimalFromFloat(0.073333333333333304), 353 "node3": num.DecimalFromFloat(0.110000000000000022), 354 "node4": num.DecimalZero(), 355 "node5": num.DecimalFromFloat(0.293333333333333304), 356 } 357 norm := normaliseScores(scores, rnd) 358 require.Equal(t, "0", norm["node1"].String()) 359 require.Equal(t, "0.1538461538461538", norm["node2"].String()) 360 require.Equal(t, "0.2307692307692308", norm["node3"].String()) 361 require.Equal(t, "0", norm["node4"].String()) 362 require.Equal(t, "0.6153846153846154", norm["node5"].String()) 363 364 total := num.DecimalZero() 365 for _, d := range norm { 366 total = total.Add(d) 367 } 368 require.True(t, total.LessThanOrEqual(num.DecimalFromFloat(1))) 369 } 370 371 func testValidatorScore(t *testing.T) { 372 validatorStake := num.DecimalFromInt64(10000) 373 largeValidatorStake := num.DecimalFromInt64(40000) 374 extraLargeValidatorStake := num.DecimalFromInt64(60000) 375 extraExtraLargeValidatorStake := num.DecimalFromInt64(70000) 376 totalStake := num.DecimalFromInt64(100000.0) 377 minVal := num.DecimalFromInt64(5) 378 compLevel, _ := num.DecimalFromString("1.1") 379 optimalStakeMultiplier, _ := num.DecimalFromString("3.0") 380 381 stakeScoreParams := types.StakeScoreParams{ 382 MinVal: minVal, 383 CompLevel: compLevel, 384 OptimalStakeMultiplier: optimalStakeMultiplier, 385 } 386 387 // valStake = 10k, totalStake = 100k, optStake = 20k 388 // valScore = 0.1 389 require.Equal(t, "0.10", CalcValidatorScore(validatorStake, totalStake, num.DecimalFromInt64(20000), stakeScoreParams).StringFixed(2)) 390 391 // valStake = 20k, totalStake = 100k, optStake = 20k 392 // valScore = 0.2 393 // no pentalty 394 require.Equal(t, "0.20", CalcValidatorScore(largeValidatorStake, totalStake, num.DecimalFromInt64(20000), stakeScoreParams).StringFixed(2)) 395 396 // valStake = 60k, totalStake = 100k, optStake = 20k 397 // valScore = 0.2 398 // with flat pentalty 399 require.Equal(t, "0.20", CalcValidatorScore(extraLargeValidatorStake, totalStake, num.DecimalFromInt64(20000), stakeScoreParams).StringFixed(2)) 400 401 // valStake = 70k, totalStake = 100k, optStake = 20k 402 // valScore = 0.1 403 // with flat and down pentalty 404 require.Equal(t, "0.10", CalcValidatorScore(extraExtraLargeValidatorStake, totalStake, num.DecimalFromInt64(20000), stakeScoreParams).StringFixed(2)) 405 406 // no stake => 0 407 require.Equal(t, "0.00", CalcValidatorScore(num.DecimalZero(), num.DecimalZero(), num.DecimalFromInt64(20000), stakeScoreParams).StringFixed(2)) 408 } 409 410 func testGetValScore(t *testing.T) { 411 stakeScore := map[string]num.Decimal{ 412 "node1": num.DecimalFromFloat(0.1), 413 "node2": num.DecimalFromFloat(0.2), 414 "node3": num.DecimalFromFloat(0.3), 415 "node4": num.DecimalFromFloat(0.4), 416 "node5": num.DecimalFromFloat(0.5), 417 } 418 perfScore := map[string]num.Decimal{ 419 "node1": num.DecimalFromFloat(0.5), 420 "node2": num.DecimalFromFloat(0.6), 421 "node3": num.DecimalFromFloat(0.7), 422 "node4": num.DecimalFromFloat(0.8), 423 "node5": num.DecimalFromFloat(0.9), 424 } 425 426 valScore := getValScore(stakeScore, perfScore) 427 require.Equal(t, "0.05", valScore["node1"].String()) 428 require.Equal(t, "0.12", valScore["node2"].String()) 429 require.Equal(t, "0.21", valScore["node3"].String()) 430 require.Equal(t, "0.32", valScore["node4"].String()) 431 require.Equal(t, "0.45", valScore["node5"].String()) 432 } 433 434 func testGetMultisigScore(t *testing.T) { 435 stakeScore := map[string]num.Decimal{ 436 "node1": num.DecimalFromFloat(0.1), 437 "node2": num.DecimalFromFloat(0.2), 438 "node3": num.DecimalFromFloat(0.3), 439 "node4": num.DecimalFromFloat(0.4), 440 "node5": num.DecimalFromFloat(0.5), 441 "node6": num.DecimalFromFloat(0.55), 442 "node7": num.DecimalFromFloat(0.6), 443 "node8": num.DecimalFromFloat(0.65), 444 "node9": num.DecimalFromFloat(0.7), 445 "node10": num.DecimalFromFloat(0.75), 446 } 447 perfScore := map[string]num.Decimal{ 448 "node1": num.DecimalFromFloat(0.5), 449 "node2": num.DecimalFromFloat(0.6), 450 "node3": num.DecimalFromFloat(0.7), 451 "node4": num.DecimalFromFloat(0.8), 452 "node5": num.DecimalFromFloat(0.9), 453 "node6": num.DecimalFromFloat(0.9), 454 "node7": num.DecimalFromFloat(0.9), 455 "node8": num.DecimalFromFloat(0.9), 456 "node9": num.DecimalFromFloat(0.9), 457 "node10": num.DecimalFromFloat(0.9), 458 } 459 460 multisigValidators := map[string]struct{}{ 461 "node1eth": {}, 462 "node2eth": {}, 463 "node5eth": {}, 464 "node7eth": {}, 465 "node8eth": {}, 466 "node9eth": {}, 467 "node10eth": {}, 468 } 469 470 nodeIDToEthAddress := map[string]string{ 471 "node1": "node1eth", 472 "node2": "node2eth", 473 "node3": "node3eth", 474 "node4": "node4eth", 475 "node5": "node5eth", 476 "node6": "node6eth", 477 "node7": "node7eth", 478 "node8": "node8eth", 479 "node9": "node9eth", 480 "node10": "node10eth", 481 } 482 483 multisigValidators2 := map[string]struct{}{} 484 maps.Copy(multisigValidators2, multisigValidators) 485 486 mtop1 := &TestMultisigTopology{ 487 validators: multisigValidators, 488 } 489 mtop2 := &TestMultisigTopology{ 490 validators: multisigValidators2, 491 } 492 493 log := logging.NewTestLogger() 494 multisigScore := getMultisigScore( 495 log, 496 ValidatorStatusTendermint, 497 stakeScore, perfScore, mtop1, mtop2, 5, 498 nodeIDToEthAddress, 499 ) 500 501 // sorted by the score = stake x performance node 10 is the top and node 1 is the bottom. 502 // looking at the top 5 that gives node10 - node6 503 // out of those node 10,9,8,7 are in the multisig set 504 // node 6 is not so it gets a multisig score of 0 505 // all the other nodes are not required to be so their multisig score is 1. 506 require.Equal(t, "1", multisigScore["node1"].String()) 507 require.Equal(t, "1", multisigScore["node2"].String()) 508 require.Equal(t, "1", multisigScore["node3"].String()) 509 require.Equal(t, "1", multisigScore["node4"].String()) 510 require.Equal(t, "1", multisigScore["node5"].String()) 511 require.Equal(t, "0", multisigScore["node6"].String()) 512 require.Equal(t, "1", multisigScore["node7"].String()) 513 require.Equal(t, "1", multisigScore["node8"].String()) 514 require.Equal(t, "1", multisigScore["node9"].String()) 515 require.Equal(t, "1", multisigScore["node10"].String()) 516 517 // node 6 is added to one bridge but not the other 518 multisigValidators["node6"] = struct{}{} 519 multisigScore = getMultisigScore( 520 log, 521 ValidatorStatusTendermint, 522 stakeScore, perfScore, mtop1, mtop2, 5, 523 nodeIDToEthAddress, 524 ) 525 require.Equal(t, "0", multisigScore["node6"].String()) 526 527 // node6 is added to the second bridge 528 multisigValidators2["node6"] = struct{}{} 529 multisigScore = getMultisigScore( 530 log, 531 ValidatorStatusTendermint, 532 stakeScore, perfScore, mtop1, mtop2, 5, 533 nodeIDToEthAddress, 534 ) 535 require.Equal(t, "0", multisigScore["node6"].String()) 536 537 // Add a node to *one* bridge that shouldn't be there, even if its not on the other one 538 // all scores should be zero 539 multisigValidators["node100"] = struct{}{} 540 nodeIDToEthAddress["node100"] = "node100eth" 541 542 // everyone gets zero scores because there is someone on there who shouldn't be 543 multisigScore = getMultisigScore(log, ValidatorStatusTendermint, stakeScore, perfScore, mtop1, mtop2, 5, nodeIDToEthAddress) 544 require.Equal(t, "0", multisigScore["node1"].String()) 545 require.Equal(t, "0", multisigScore["node2"].String()) 546 require.Equal(t, "0", multisigScore["node3"].String()) 547 require.Equal(t, "0", multisigScore["node4"].String()) 548 require.Equal(t, "0", multisigScore["node5"].String()) 549 require.Equal(t, "0", multisigScore["node6"].String()) 550 require.Equal(t, "0", multisigScore["node7"].String()) 551 require.Equal(t, "0", multisigScore["node8"].String()) 552 require.Equal(t, "0", multisigScore["node9"].String()) 553 require.Equal(t, "0", multisigScore["node10"].String()) 554 } 555 556 func TestGetMultisigScoreMoreValidatorsThanSigners(t *testing.T) { 557 // 5 nodes all with an equal score, and none of them on the contract. We also set the number of signers we check to only 2 558 // normally in this case we check the 2 nodes with the highest score, but when they are all equal we *should* instead sort 559 // by nodeID 560 nEthMultisigSigners := 2 561 stakeScore := map[string]num.Decimal{ 562 "node1": num.DecimalFromFloat(0.1), 563 "node2": num.DecimalFromFloat(0.1), 564 "node3": num.DecimalFromFloat(0.1), 565 "node4": num.DecimalFromFloat(0.1), 566 "node5": num.DecimalFromFloat(0.1), 567 } 568 perfScore := map[string]num.Decimal{ 569 "node1": num.DecimalFromFloat(0.1), 570 "node2": num.DecimalFromFloat(0.1), 571 "node3": num.DecimalFromFloat(0.1), 572 "node4": num.DecimalFromFloat(0.1), 573 "node5": num.DecimalFromFloat(0.1), 574 } 575 576 nodeIDToEthAddress := map[string]string{ 577 "node1": "node1eth", 578 "node2": "node2eth", 579 "node3": "node3eth", 580 "node4": "node4eth", 581 "node5": "node5eth", 582 } 583 584 mtop1 := &TestMultisigTopology{ 585 validators: map[string]struct{}{}, 586 } 587 mtop2 := &TestMultisigTopology{ 588 validators: map[string]struct{}{}, 589 } 590 591 log := logging.NewTestLogger() 592 593 for i := 0; i < 100; i++ { 594 multisigScore := getMultisigScore( 595 log, 596 ValidatorStatusTendermint, 597 stakeScore, perfScore, mtop1, mtop2, nEthMultisigSigners, 598 nodeIDToEthAddress, 599 ) 600 require.Equal(t, "0", multisigScore["node1"].String()) 601 require.Equal(t, "0", multisigScore["node2"].String()) 602 require.Equal(t, "1", multisigScore["node3"].String()) 603 require.Equal(t, "1", multisigScore["node4"].String()) 604 require.Equal(t, "1", multisigScore["node5"].String()) 605 } 606 } 607 608 func testCalculateTMScores(t *testing.T) { 609 topology := &Topology{} 610 topology.validators = map[string]*valState{} 611 topology.log = logging.NewTestLogger() 612 613 for i := 0; i < 10; i++ { 614 index := num.NewUint(uint64(i) + 1).String() 615 topology.validators["node"+index] = &valState{ 616 data: ValidatorData{ 617 ID: "node" + index, 618 TmPubKey: "key" + index, 619 EthereumAddress: "node" + index + "eth", 620 }, 621 status: ValidatorStatusTendermint, 622 heartbeatTracker: &validatorHeartbeatTracker{}, 623 } 624 } 625 topology.validators["node11"] = &valState{ 626 data: ValidatorData{ 627 ID: "node11", 628 EthereumAddress: "node11eth", 629 }, 630 status: ValidatorStatusErsatz, 631 heartbeatTracker: &validatorHeartbeatTracker{}, 632 } 633 topology.validators["node12"] = &valState{ 634 data: ValidatorData{ 635 ID: "node12", 636 EthereumAddress: "node12eth", 637 }, 638 status: ValidatorStatusErsatz, 639 heartbeatTracker: &validatorHeartbeatTracker{}, 640 } 641 642 topology.validatorIncumbentBonusFactor = num.DecimalFromFloat(1.1) 643 topology.minimumStake = num.NewUint(3000) 644 topology.validatorPerformance = &MockPerformanceScore{} 645 topology.numberEthMultisigSigners = 7 646 647 mtop1 := &TestMultisigTopology{} 648 mtop1.validators = map[string]struct{}{ 649 "node1eth": {}, 650 "node2eth": {}, 651 "node3eth": {}, 652 "node5eth": {}, 653 "node7eth": {}, 654 "node9eth": {}, 655 } 656 mtop2 := &TestMultisigTopology{} 657 mtop2.validators = map[string]struct{}{ 658 "node1eth": {}, 659 "node2eth": {}, 660 "node3eth": {}, 661 "node5eth": {}, 662 "node7eth": {}, 663 "node9eth": {}, 664 } 665 topology.primaryMultisig = mtop1 666 topology.secondaryMultisig = mtop2 667 668 delegation := []*types.ValidatorData{ 669 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 670 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 671 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 672 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 673 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 674 {NodeID: "node6", PubKey: "node6PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key6"}, 675 {NodeID: "node7", PubKey: "node7PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(6000), Delegators: map[string]*num.Uint{}, TmPubKey: "key7"}, 676 {NodeID: "node8", PubKey: "node8PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(5000), Delegators: map[string]*num.Uint{}, TmPubKey: "key8"}, 677 {NodeID: "node9", PubKey: "node9PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(4000), Delegators: map[string]*num.Uint{}, TmPubKey: "key9"}, 678 {NodeID: "node10", PubKey: "node10PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key10"}, 679 {NodeID: "node11", PubKey: "node11PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key11"}, 680 {NodeID: "node12", PubKey: "node12PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(1000), Delegators: map[string]*num.Uint{}, TmPubKey: "key12"}, 681 } 682 scoreData, _ := topology.calculateScores(delegation, ValidatorStatusTendermint, types.StakeScoreParams{MinVal: num.DecimalFromFloat(5), CompLevel: num.DecimalFromFloat(3.3), OptimalStakeMultiplier: num.DecimalFromFloat(3)}, nil) 683 684 require.Equal(t, 10, len(scoreData.RawValScores)) 685 require.Equal(t, 10, len(scoreData.PerformanceScores)) 686 require.Equal(t, 10, len(scoreData.MultisigScores)) 687 require.Equal(t, 10, len(scoreData.ValScores)) 688 require.Equal(t, 10, len(scoreData.NormalisedScores)) 689 690 // raw scores 691 // total = 110000 692 // node1 = 10000/110000 = 0.09090909091 693 // node2 = 5000/110000 = 0.04545454545 694 // node3 = 10000/110000 = 0.09090909091 695 // node4 = 15000/110000 = 0.1363636364 696 // node5 = 20000/110000 = 0.1818181818 697 // node6 = 12000/110000 = 0.1090909091 698 // node7 = 11000/110000 = 0.1 699 // node8 = 10000/110000 = 0.09090909091 700 // node9 = 9000/110000 = 0.08181818182 701 // node10 = 8000/110000 = 0.07272727273 702 require.Equal(t, "0.09090909", scoreData.RawValScores["node1"].StringFixed(8)) 703 require.Equal(t, "0.04545455", scoreData.RawValScores["node2"].StringFixed(8)) 704 require.Equal(t, "0.09090909", scoreData.RawValScores["node3"].StringFixed(8)) 705 require.Equal(t, "0.13636364", scoreData.RawValScores["node4"].StringFixed(8)) 706 require.Equal(t, "0.18181818", scoreData.RawValScores["node5"].StringFixed(8)) 707 require.Equal(t, "0.10909091", scoreData.RawValScores["node6"].StringFixed(8)) 708 require.Equal(t, "0.10000000", scoreData.RawValScores["node7"].StringFixed(8)) 709 require.Equal(t, "0.09090909", scoreData.RawValScores["node8"].StringFixed(8)) 710 require.Equal(t, "0.08181818", scoreData.RawValScores["node9"].StringFixed(8)) 711 require.Equal(t, "0.07272727", scoreData.RawValScores["node10"].StringFixed(8)) 712 713 // performance score 714 // node1 has less than the minimum self stake => 0 715 // node2-5 0.8 716 // node6 0.3 717 // node7 0.7 718 // node8 0.3 719 // node9 0.7 720 // node10 1 721 require.Equal(t, "0", scoreData.PerformanceScores["node1"].String()) 722 require.Equal(t, "0.8", scoreData.PerformanceScores["node2"].String()) 723 require.Equal(t, "0.8", scoreData.PerformanceScores["node3"].String()) 724 require.Equal(t, "0.8", scoreData.PerformanceScores["node4"].String()) 725 require.Equal(t, "0.8", scoreData.PerformanceScores["node5"].String()) 726 require.Equal(t, "0.3", scoreData.PerformanceScores["node6"].String()) 727 require.Equal(t, "0.7", scoreData.PerformanceScores["node7"].String()) 728 require.Equal(t, "0.3", scoreData.PerformanceScores["node8"].String()) 729 require.Equal(t, "0.7", scoreData.PerformanceScores["node9"].String()) 730 require.Equal(t, "1", scoreData.PerformanceScores["node10"].String()) 731 732 // multisig score 733 // stake_score x performance_score: 734 // node1 = 0 735 // node2 = 0.04545454545 * 0.8 = 0.03636363636 736 // node3= 0.09090909091 * 0.8 = 0.07272727273 737 // node4 = 0.1363636364 * 0.8 = 0.1090909091 738 // node5 = 0.1818181818 * 0.8 = 0.1454545454 739 // node6 = 0.1090909091 * 0.3 = 0.03272727273 740 // node7 = 0.1 * 0.7 = 0.07 741 // node8 = 0.09090909091 * 0.3 = 0.02727272727 742 // node9 = 0.08181818182 * 0.7 = 0.05727272727 743 // node10 = 0.07272727273 * 1 = 0.07272727273 744 // sorted order is: 745 // node5, node4, node3, node10, node7, node9, node2, node6, node8, node1 746 // the net param is set to 7 so we're looking at the top 7 scores 747 require.Equal(t, "1", scoreData.MultisigScores["node1"].String()) 748 require.Equal(t, "1", scoreData.MultisigScores["node2"].String()) 749 require.Equal(t, "1", scoreData.MultisigScores["node3"].String()) 750 require.Equal(t, "0", scoreData.MultisigScores["node4"].String()) 751 require.Equal(t, "1", scoreData.MultisigScores["node5"].String()) 752 require.Equal(t, "1", scoreData.MultisigScores["node6"].String()) 753 require.Equal(t, "1", scoreData.MultisigScores["node7"].String()) 754 require.Equal(t, "1", scoreData.MultisigScores["node8"].String()) 755 require.Equal(t, "1", scoreData.MultisigScores["node9"].String()) 756 require.Equal(t, "0", scoreData.MultisigScores["node10"].String()) 757 758 // val scores = stake_score * perf_score * multisig_score 759 require.Equal(t, "0", scoreData.ValScores["node1"].String()) 760 require.Equal(t, "0.03636363636", scoreData.ValScores["node2"].StringFixed(11)) 761 require.Equal(t, "0.07272727273", scoreData.ValScores["node3"].StringFixed(11)) 762 require.Equal(t, "0", scoreData.ValScores["node4"].String()) 763 require.Equal(t, "0.14545454545", scoreData.ValScores["node5"].StringFixed(11)) 764 require.Equal(t, "0.03272727272727273", scoreData.ValScores["node6"].String()) 765 require.Equal(t, "0.07", scoreData.ValScores["node7"].StringFixed(2)) 766 require.Equal(t, "0.02727272727272727", scoreData.ValScores["node8"].String()) 767 require.Equal(t, "0.05727272727", scoreData.ValScores["node9"].StringFixed(11)) 768 require.Equal(t, "0", scoreData.ValScores["node10"].String()) 769 770 // normalised scores 771 // node2 = 0.03636363636 / 0.3818181818 = 0.09523809523 772 // node3 = 0.07272727273 / 0.3818181818 = 0.1904761905 773 // node5 = 0.14545454545 / 0.3818181818 = 0.380952381 774 // node7 = 0.07 / 0.3818181818 = 0.1833333333 775 // node9 = 0.05727272727 / 0.3818181818 = 0.15 776 require.Equal(t, "0.08230452675", scoreData.NormalisedScores["node2"].StringFixed(11)) 777 require.Equal(t, "0.16460905350", scoreData.NormalisedScores["node3"].StringFixed(11)) 778 require.Equal(t, "0.32921810700", scoreData.NormalisedScores["node5"].StringFixed(11)) 779 require.Equal(t, "0.15843621399", scoreData.NormalisedScores["node7"].StringFixed(11)) 780 require.Equal(t, "0.13", scoreData.NormalisedScores["node9"].StringFixed(2)) 781 782 totalNormScore := num.DecimalZero() 783 for _, d := range scoreData.NormalisedScores { 784 totalNormScore = totalNormScore.Add(d) 785 } 786 require.True(t, totalNormScore.LessThanOrEqual(decimalOne)) 787 } 788 789 func testCalculateErsatzScores(t *testing.T) { 790 topology := &Topology{} 791 topology.log = logging.NewTestLogger() 792 topology.validators = map[string]*valState{} 793 794 for i := 0; i < 10; i++ { 795 index := num.NewUint(uint64(i) + 1).String() 796 topology.validators["node"+index] = &valState{ 797 data: ValidatorData{ 798 ID: "node" + index, 799 EthereumAddress: "node" + index + "eth", 800 }, 801 status: ValidatorStatusErsatz, 802 heartbeatTracker: &validatorHeartbeatTracker{}, 803 numberOfEthereumEventsForwarded: 4, 804 } 805 for j := 0; j < i; j++ { 806 topology.validators["node"+index].heartbeatTracker.blockSigs[j] = true 807 } 808 } 809 topology.validators["node11"] = &valState{ 810 data: ValidatorData{ 811 ID: "node11", 812 EthereumAddress: "node11eth", 813 }, 814 status: ValidatorStatusTendermint, 815 heartbeatTracker: &validatorHeartbeatTracker{}, 816 } 817 topology.validators["node12"] = &valState{ 818 data: ValidatorData{ 819 ID: "node12", 820 EthereumAddress: "node12eth", 821 }, 822 status: ValidatorStatusTendermint, 823 heartbeatTracker: &validatorHeartbeatTracker{}, 824 } 825 826 topology.validatorIncumbentBonusFactor = num.DecimalFromFloat(1.1) 827 topology.minimumStake = num.NewUint(3000) 828 topology.validatorPerformance = &MockPerformanceScore{} 829 topology.numberEthMultisigSigners = 7 830 topology.primaryMultisig = &TestMultisigTopology{ 831 validators: map[string]struct{}{ 832 "node1eth": {}, 833 "node2eth": {}, 834 "node3eth": {}, 835 "node5eth": {}, 836 "node7eth": {}, 837 "node9eth": {}, 838 }, 839 } 840 841 delegation := []*types.ValidatorData{ 842 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 843 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 844 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 845 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 846 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 847 {NodeID: "node6", PubKey: "node6PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key6"}, 848 {NodeID: "node7", PubKey: "node7PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(6000), Delegators: map[string]*num.Uint{}, TmPubKey: "key7"}, 849 {NodeID: "node8", PubKey: "node8PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(5000), Delegators: map[string]*num.Uint{}, TmPubKey: "key8"}, 850 {NodeID: "node9", PubKey: "node9PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(4000), Delegators: map[string]*num.Uint{}, TmPubKey: "key9"}, 851 {NodeID: "node10", PubKey: "node10PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key10"}, 852 {NodeID: "node11", PubKey: "node11PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key11"}, 853 {NodeID: "node12", PubKey: "node12PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(1000), Delegators: map[string]*num.Uint{}, TmPubKey: "key12"}, 854 } 855 topology.rng = rand.New(rand.NewSource(100000)) 856 optimalScore := num.DecimalFromInt64(10000) 857 scoreData, _ := topology.calculateScores(delegation, ValidatorStatusErsatz, types.StakeScoreParams{MinVal: num.DecimalFromFloat(5), CompLevel: num.DecimalFromFloat(3.3), OptimalStakeMultiplier: num.DecimalFromFloat(5)}, &optimalScore) 858 859 require.Equal(t, 10, len(scoreData.RawValScores)) 860 require.Equal(t, 10, len(scoreData.PerformanceScores)) 861 require.Equal(t, 10, len(scoreData.MultisigScores)) 862 require.Equal(t, 10, len(scoreData.ValScores)) 863 require.Equal(t, 10, len(scoreData.NormalisedScores)) 864 865 // raw scores 866 // total = 110000 867 // opt stake = 10000 868 // node1 = 10000/110000 = 0.09090909091 869 // node2 = 5000/110000 = 0.04545454545 870 // node3 = 10000/110000 = 0.09090909091 871 // node4 = 15000/110000 = 10000/110000 = 0.09090909091 (with flat penalty) 872 // node5 = 20000/110000 = 10000/110000 = 0.09090909091 (with flat penalty) 873 // node6 = 12000/110000 = 10000/110000 = 0.09090909091 (with flat penalty) 874 // node7 = 11000/110000 = 10000/110000 = 0.09090909091 (with flat penalty) 875 // node8 = 10000/110000 = 0.09090909091 876 // node9 = 9000/110000 = 0.08181818182 877 // node10 = 8000/110000 = 0.07272727273 878 require.Equal(t, "0.09090909", scoreData.RawValScores["node1"].StringFixed(8)) 879 require.Equal(t, "0.04545455", scoreData.RawValScores["node2"].StringFixed(8)) 880 require.Equal(t, "0.09090909", scoreData.RawValScores["node3"].StringFixed(8)) 881 require.Equal(t, "0.09090909", scoreData.RawValScores["node4"].StringFixed(8)) 882 require.Equal(t, "0.09090909", scoreData.RawValScores["node5"].StringFixed(8)) 883 require.Equal(t, "0.09090909", scoreData.RawValScores["node6"].StringFixed(8)) 884 require.Equal(t, "0.09090909", scoreData.RawValScores["node7"].StringFixed(8)) 885 require.Equal(t, "0.09090909", scoreData.RawValScores["node8"].StringFixed(8)) 886 require.Equal(t, "0.08181818", scoreData.RawValScores["node9"].StringFixed(8)) 887 require.Equal(t, "0.07272727", scoreData.RawValScores["node10"].StringFixed(8)) 888 889 // performance score 890 // node1 0 891 // node2 0.1 892 // node3 0.2 893 // node4 0.3 894 // node5 0.4 895 // node6 0.5 896 // node7 0.6 897 // node8 0.7 898 // node9 0.8 899 // node10 0.9 900 require.Equal(t, "0", scoreData.PerformanceScores["node1"].String()) 901 require.Equal(t, "0.1", scoreData.PerformanceScores["node2"].String()) 902 require.Equal(t, "0.2", scoreData.PerformanceScores["node3"].String()) 903 require.Equal(t, "0.3", scoreData.PerformanceScores["node4"].String()) 904 require.Equal(t, "0.4", scoreData.PerformanceScores["node5"].String()) 905 require.Equal(t, "0.5", scoreData.PerformanceScores["node6"].String()) 906 require.Equal(t, "0.6", scoreData.PerformanceScores["node7"].String()) 907 require.Equal(t, "0.7", scoreData.PerformanceScores["node8"].String()) 908 require.Equal(t, "0.8", scoreData.PerformanceScores["node9"].String()) 909 require.Equal(t, "0.9", scoreData.PerformanceScores["node10"].String()) 910 911 // multisig score 912 // not relevant for ersatz validators should all be 1 913 require.Equal(t, "1", scoreData.MultisigScores["node1"].String()) 914 require.Equal(t, "1", scoreData.MultisigScores["node2"].String()) 915 require.Equal(t, "1", scoreData.MultisigScores["node3"].String()) 916 require.Equal(t, "1", scoreData.MultisigScores["node4"].String()) 917 require.Equal(t, "1", scoreData.MultisigScores["node5"].String()) 918 require.Equal(t, "1", scoreData.MultisigScores["node6"].String()) 919 require.Equal(t, "1", scoreData.MultisigScores["node7"].String()) 920 require.Equal(t, "1", scoreData.MultisigScores["node8"].String()) 921 require.Equal(t, "1", scoreData.MultisigScores["node9"].String()) 922 require.Equal(t, "1", scoreData.MultisigScores["node10"].String()) 923 924 // val score = stake_score x performance_score: 925 // node1 = 0 926 // node2 = 0.04545454545 * 0.1 = 0.004545454545 927 // node3 = 0.09090909091 * 0.2 = 0.01818181818 928 // node4 = 0.09090909091 * 0.3 = 0.02727272727 929 // node5 = 0.09090909091 * 0.4 = 0.03636363636 930 // node6 = 0.09090909091 * 0.5 = 0.04545454545 931 // node7 = 0.09090909091 * 0.6 = 0.05454545455 932 // node8 = 0.09090909091 * 0.7 = 0.06363636364 933 // node9 = 0.08181818182 * 0.8 = 0.06545454545 934 // node10 = 0.07272727273 * 0.9 = 0.06545454545 935 936 // val scores = stake_score * perf_score * multisig_score 937 require.Equal(t, "0", scoreData.ValScores["node1"].String()) 938 require.Equal(t, "0.00454545455", scoreData.ValScores["node2"].StringFixed(11)) 939 require.Equal(t, "0.01818181818", scoreData.ValScores["node3"].StringFixed(11)) 940 require.Equal(t, "0.02727272727", scoreData.ValScores["node4"].StringFixed(11)) 941 require.Equal(t, "0.03636363636", scoreData.ValScores["node5"].StringFixed(11)) 942 require.Equal(t, "0.04545454545", scoreData.ValScores["node6"].StringFixed(11)) 943 require.Equal(t, "0.05454545455", scoreData.ValScores["node7"].StringFixed(11)) 944 require.Equal(t, "0.06363636364", scoreData.ValScores["node8"].StringFixed(11)) 945 require.Equal(t, "0.06545454545", scoreData.ValScores["node9"].StringFixed(11)) 946 require.Equal(t, "0.06545454545", scoreData.ValScores["node10"].StringFixed(11)) 947 948 // normalised scores 949 // node1 = 0 950 // node2 = 0.00454545455 / 0.3809090909 = 0.01193317424 951 // node3 = 0.01818181818 / 0.3809090909 = 0.0477326969 952 // node4 = 0.02727272727 / 0.3809090909 = 0.07159904534 953 // node5 = 0.03636363636 / 0.3809090909 = 0.09546539379 954 // node6 = 0.04545454545 / 0.3809090909 = 0.1193317422 955 // node7 = 0.05454545455 / 0.3809090909 = 0.1431980907 956 // node8 = 0.06363636364 / 0.3809090909 = 0.1670644391 957 // node9 = 0.06545454545 / 0.3809090909 = 0.1718377088 958 // node10 = 0.06545454545 / 0.3809090909 = 0.1718377088 959 require.Equal(t, "0.00000000000", scoreData.NormalisedScores["node1"].StringFixed(11)) 960 require.Equal(t, "0.0119331742", scoreData.NormalisedScores["node2"].StringFixed(10)) 961 require.Equal(t, "0.0477326969", scoreData.NormalisedScores["node3"].StringFixed(10)) 962 require.Equal(t, "0.0715990453", scoreData.NormalisedScores["node4"].StringFixed(10)) 963 require.Equal(t, "0.0954653938", scoreData.NormalisedScores["node5"].StringFixed(10)) 964 require.Equal(t, "0.1193317422", scoreData.NormalisedScores["node6"].StringFixed(10)) 965 require.Equal(t, "0.1431980907", scoreData.NormalisedScores["node7"].StringFixed(10)) 966 require.Equal(t, "0.1670644391", scoreData.NormalisedScores["node8"].StringFixed(10)) 967 require.Equal(t, "0.1718377088", scoreData.NormalisedScores["node9"].StringFixed(10)) 968 require.Equal(t, "0.1718377088", scoreData.NormalisedScores["node10"].StringFixed(10)) 969 970 totalNormScore := num.DecimalZero() 971 for _, d := range scoreData.NormalisedScores { 972 totalNormScore = totalNormScore.Add(d) 973 } 974 require.True(t, totalNormScore.LessThanOrEqual(decimalOne)) 975 } 976 977 func testGetRewardsScores(t *testing.T) { 978 topology := &Topology{} 979 topology.log = logging.NewTestLogger() 980 topology.validators = map[string]*valState{} 981 topology.validatorIncumbentBonusFactor = num.DecimalFromFloat(1.1) 982 topology.minimumStake = num.NewUint(3000) 983 topology.validatorPerformance = &MockPerformanceScore{} 984 topology.numberEthMultisigSigners = 7 985 topology.primaryMultisig = &TestMultisigTopology{ 986 validators: map[string]struct{}{ 987 "node1eth": {}, 988 "node2eth": {}, 989 "node3eth": {}, 990 "node5eth": {}, 991 "node7eth": {}, 992 "node9eth": {}, 993 }, 994 } 995 topology.secondaryMultisig = &TestMultisigTopology{ 996 validators: map[string]struct{}{ 997 "node1eth": {}, 998 "node2eth": {}, 999 "node3eth": {}, 1000 "node5eth": {}, 1001 "node7eth": {}, 1002 "node9eth": {}, 1003 }, 1004 } 1005 1006 for i := 0; i < 10; i++ { 1007 index := num.NewUint(uint64(i) + 1).String() 1008 topology.validators["node"+index] = &valState{ 1009 data: ValidatorData{ 1010 ID: "node" + index, 1011 TmPubKey: "key" + index, 1012 EthereumAddress: "node" + index + "eth", 1013 }, 1014 status: ValidatorStatusTendermint, 1015 heartbeatTracker: &validatorHeartbeatTracker{}, 1016 } 1017 } 1018 for i := 10; i < 20; i++ { 1019 index := num.NewUint(uint64(i) + 1).String() 1020 topology.validators["node"+index] = &valState{ 1021 data: ValidatorData{ 1022 ID: "node" + index, 1023 }, 1024 status: ValidatorStatusErsatz, 1025 numberOfEthereumEventsForwarded: 4, 1026 heartbeatTracker: &validatorHeartbeatTracker{ 1027 blockSigs: [10]bool{}, 1028 }, 1029 } 1030 for j := 10; j < i; j++ { 1031 topology.validators["node"+index].heartbeatTracker.blockSigs[j-10] = true 1032 } 1033 } 1034 topology.rng = rand.New(rand.NewSource(100000)) 1035 ctrl := gomock.NewController(t) 1036 defer ctrl.Finish() 1037 1038 broker := bmocks.NewMockBroker(ctrl) 1039 topology.broker = broker 1040 1041 var bEvents []events.Event 1042 broker.EXPECT().SendBatch(gomock.Any()).Do(func(evnts []events.Event) { bEvents = evnts }).Times(1) 1043 1044 delegation := []*types.ValidatorData{ 1045 {NodeID: "node1", PubKey: "node1PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key1"}, 1046 {NodeID: "node2", PubKey: "node2PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key2"}, 1047 {NodeID: "node3", PubKey: "node3PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key3"}, 1048 {NodeID: "node4", PubKey: "node4PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key4"}, 1049 {NodeID: "node5", PubKey: "node5PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key5"}, 1050 {NodeID: "node6", PubKey: "node6PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key6"}, 1051 {NodeID: "node7", PubKey: "node7PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(6000), Delegators: map[string]*num.Uint{}, TmPubKey: "key7"}, 1052 {NodeID: "node8", PubKey: "node8PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(5000), Delegators: map[string]*num.Uint{}, TmPubKey: "key8"}, 1053 {NodeID: "node9", PubKey: "node9PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(4000), Delegators: map[string]*num.Uint{}, TmPubKey: "key9"}, 1054 {NodeID: "node10", PubKey: "node10PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key10"}, 1055 {NodeID: "node11", PubKey: "node11PubKey", StakeByDelegators: num.NewUint(8000), SelfStake: num.NewUint(2000), Delegators: map[string]*num.Uint{}, TmPubKey: "key11"}, 1056 {NodeID: "node12", PubKey: "node12PubKey", StakeByDelegators: num.NewUint(2000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key12"}, 1057 {NodeID: "node13", PubKey: "node13PubKey", StakeByDelegators: num.NewUint(3000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key13"}, 1058 {NodeID: "node14", PubKey: "node14PubKey", StakeByDelegators: num.NewUint(4000), SelfStake: num.NewUint(11000), Delegators: map[string]*num.Uint{}, TmPubKey: "key14"}, 1059 {NodeID: "node15", PubKey: "node15PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(15000), Delegators: map[string]*num.Uint{}, TmPubKey: "key15"}, 1060 {NodeID: "node16", PubKey: "node16PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(7000), Delegators: map[string]*num.Uint{}, TmPubKey: "key16"}, 1061 {NodeID: "node17", PubKey: "node17PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(6000), Delegators: map[string]*num.Uint{}, TmPubKey: "key17"}, 1062 {NodeID: "node18", PubKey: "node18PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(5000), Delegators: map[string]*num.Uint{}, TmPubKey: "key18"}, 1063 {NodeID: "node19", PubKey: "node19PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(4000), Delegators: map[string]*num.Uint{}, TmPubKey: "key19"}, 1064 {NodeID: "node20", PubKey: "node20PubKey", StakeByDelegators: num.NewUint(5000), SelfStake: num.NewUint(3000), Delegators: map[string]*num.Uint{}, TmPubKey: "key20"}, 1065 } 1066 1067 tmScores, ezScores := topology.GetRewardsScores(context.Background(), "1", delegation, types.StakeScoreParams{MinVal: num.DecimalFromFloat(5), CompLevel: num.DecimalFromFloat(3.3), OptimalStakeMultiplier: num.DecimalFromFloat(5)}) 1068 1069 // tendermint 1070 require.Equal(t, "0.00000000000", tmScores.NormalisedScores["node1"].StringFixed(11)) 1071 require.Equal(t, "0.08230452675", tmScores.NormalisedScores["node2"].StringFixed(11)) 1072 require.Equal(t, "0.16460905350", tmScores.NormalisedScores["node3"].StringFixed(11)) 1073 require.Equal(t, "0.00000000000", tmScores.NormalisedScores["node4"].StringFixed(11)) 1074 require.Equal(t, "0.32921810700", tmScores.NormalisedScores["node5"].StringFixed(11)) 1075 require.Equal(t, "0.07407407407", tmScores.NormalisedScores["node6"].StringFixed(11)) 1076 require.Equal(t, "0.15843621399", tmScores.NormalisedScores["node7"].StringFixed(11)) 1077 require.Equal(t, "0.06172839506", tmScores.NormalisedScores["node8"].StringFixed(11)) 1078 require.Equal(t, "0.13", tmScores.NormalisedScores["node9"].StringFixed(2)) 1079 require.Equal(t, "0.00000000000", tmScores.NormalisedScores["node10"].StringFixed(11)) 1080 1081 // ersatz 1082 require.Equal(t, "0.00000000000", ezScores.NormalisedScores["node11"].StringFixed(11)) 1083 require.Equal(t, "0.01020408163", ezScores.NormalisedScores["node12"].StringFixed(11)) 1084 require.Equal(t, "0.04081632653", ezScores.NormalisedScores["node13"].StringFixed(11)) 1085 require.Equal(t, "0.09183673469", ezScores.NormalisedScores["node14"].StringFixed(11)) 1086 require.Equal(t, "0.1632653061", ezScores.NormalisedScores["node15"].StringFixed(10)) 1087 require.Equal(t, "0.1224489796", ezScores.NormalisedScores["node16"].StringFixed(10)) 1088 require.Equal(t, "0.1346938776", ezScores.NormalisedScores["node17"].StringFixed(10)) 1089 require.Equal(t, "0.1428571429", ezScores.NormalisedScores["node18"].StringFixed(10)) 1090 require.Equal(t, "0.1469387755", ezScores.NormalisedScores["node19"].StringFixed(10)) 1091 require.Equal(t, "0.1469387755", ezScores.NormalisedScores["node20"].StringFixed(10)) 1092 1093 require.Equal(t, 20, len(bEvents)) 1094 verifyEvent(t, events.NewValidatorScore(context.Background(), "node1", "1", num.DecimalZero(), num.DecimalZero(), num.MustDecimalFromString("0.0909090909090909"), num.DecimalZero(), num.DecimalFromFloat(1), "tendermint"), bEvents[0].(*events.ValidatorScore)) 1095 verifyEvent(t, events.NewValidatorScore(context.Background(), "node10", "1", num.DecimalZero(), num.DecimalZero(), num.MustDecimalFromString("0.0727272727272727"), num.DecimalFromFloat(1), num.DecimalZero(), "tendermint"), bEvents[1].(*events.ValidatorScore)) 1096 verifyEvent(t, events.NewValidatorScore(context.Background(), "node2", "1", num.MustDecimalFromString("0.0363636363636364"), num.MustDecimalFromString("0.0823045267489713"), num.MustDecimalFromString("0.0454545454545455"), num.DecimalFromFloat(0.8), num.DecimalFromFloat(1), "tendermint"), bEvents[2].(*events.ValidatorScore)) 1097 verifyEvent(t, events.NewValidatorScore(context.Background(), "node3", "1", num.MustDecimalFromString("0.07272727272727272"), num.MustDecimalFromString("0.1646090534979424"), num.MustDecimalFromString("0.0909090909090909"), num.DecimalFromFloat(0.8), num.DecimalFromFloat(1), "tendermint"), bEvents[3].(*events.ValidatorScore)) 1098 verifyEvent(t, events.NewValidatorScore(context.Background(), "node4", "1", num.DecimalZero(), num.DecimalZero(), num.MustDecimalFromString("0.1363636363636364"), num.DecimalFromFloat(0.8), num.DecimalFromFloat(0), "tendermint"), bEvents[4].(*events.ValidatorScore)) 1099 verifyEvent(t, events.NewValidatorScore(context.Background(), "node5", "1", num.MustDecimalFromString("0.14545454545454544"), num.MustDecimalFromString("0.3292181069958847"), num.MustDecimalFromString("0.1818181818181818"), num.DecimalFromFloat(0.8), num.DecimalFromFloat(1), "tendermint"), bEvents[5].(*events.ValidatorScore)) 1100 verifyEvent(t, events.NewValidatorScore(context.Background(), "node6", "1", num.MustDecimalFromString("0.03272727272727273"), num.MustDecimalFromString("0.0740740740740741"), num.MustDecimalFromString("0.1090909090909091"), num.DecimalFromFloat(0.3), num.DecimalFromFloat(1), "tendermint"), bEvents[6].(*events.ValidatorScore)) 1101 verifyEvent(t, events.NewValidatorScore(context.Background(), "node7", "1", num.MustDecimalFromString("0.07"), num.MustDecimalFromString("0.1584362139917695"), num.MustDecimalFromString("0.1"), num.DecimalFromFloat(0.7), num.DecimalFromFloat(1), "tendermint"), bEvents[7].(*events.ValidatorScore)) 1102 verifyEvent(t, events.NewValidatorScore(context.Background(), "node8", "1", num.MustDecimalFromString("0.02727272727272727"), num.MustDecimalFromString("0.0617283950617284"), num.MustDecimalFromString("0.0909090909090909"), num.DecimalFromFloat(0.3), num.DecimalFromFloat(1), "tendermint"), bEvents[8].(*events.ValidatorScore)) 1103 verifyEvent(t, events.NewValidatorScore(context.Background(), "node9", "1", num.MustDecimalFromString("0.05727272727272726"), num.MustDecimalFromString("0.1296296296296296"), num.MustDecimalFromString("0.0818181818181818"), num.DecimalFromFloat(0.7), num.DecimalFromFloat(1), "tendermint"), bEvents[9].(*events.ValidatorScore)) 1104 verifyEvent(t, events.NewValidatorScore(context.Background(), "node11", "1", num.DecimalZero(), num.DecimalZero(), num.MustDecimalFromString("0.0909090909090909"), num.DecimalZero(), num.DecimalFromFloat(1), "ersatz"), bEvents[10].(*events.ValidatorScore)) 1105 verifyEvent(t, events.NewValidatorScore(context.Background(), "node12", "1", num.MustDecimalFromString("0.00454545454545455"), num.MustDecimalFromString("0.0102040816326531"), num.MustDecimalFromString("0.0454545454545455"), num.DecimalFromFloat(0.1), num.DecimalFromFloat(1), "ersatz"), bEvents[11].(*events.ValidatorScore)) 1106 verifyEvent(t, events.NewValidatorScore(context.Background(), "node13", "1", num.MustDecimalFromString("0.01818181818181818"), num.MustDecimalFromString("0.0408163265306122"), num.MustDecimalFromString("0.0909090909090909"), num.DecimalFromFloat(0.2), num.DecimalFromFloat(1), "ersatz"), bEvents[12].(*events.ValidatorScore)) 1107 verifyEvent(t, events.NewValidatorScore(context.Background(), "node14", "1", num.MustDecimalFromString("0.04090909090909092"), num.MustDecimalFromString("0.0918367346938776"), num.MustDecimalFromString("0.1363636363636364"), num.DecimalFromFloat(0.3), num.DecimalFromFloat(1), "ersatz"), bEvents[13].(*events.ValidatorScore)) 1108 verifyEvent(t, events.NewValidatorScore(context.Background(), "node15", "1", num.MustDecimalFromString("0.07272727272727272"), num.MustDecimalFromString("0.163265306122449"), num.MustDecimalFromString("0.1818181818181818"), num.DecimalFromFloat(0.4), num.DecimalFromFloat(1), "ersatz"), bEvents[14].(*events.ValidatorScore)) 1109 verifyEvent(t, events.NewValidatorScore(context.Background(), "node16", "1", num.MustDecimalFromString("0.05454545454545455"), num.MustDecimalFromString("0.1224489795918368"), num.MustDecimalFromString("0.1090909090909091"), num.DecimalFromFloat(0.5), num.DecimalFromFloat(1), "ersatz"), bEvents[15].(*events.ValidatorScore)) 1110 verifyEvent(t, events.NewValidatorScore(context.Background(), "node17", "1", num.MustDecimalFromString("0.06"), num.MustDecimalFromString("0.1346938775510204"), num.MustDecimalFromString("0.1"), num.DecimalFromFloat(0.6), num.DecimalFromFloat(1), "ersatz"), bEvents[16].(*events.ValidatorScore)) 1111 verifyEvent(t, events.NewValidatorScore(context.Background(), "node18", "1", num.MustDecimalFromString("0.06363636363636363"), num.MustDecimalFromString("0.1428571428571429"), num.MustDecimalFromString("0.0909090909090909"), num.DecimalFromFloat(0.7), num.DecimalFromFloat(1), "ersatz"), bEvents[17].(*events.ValidatorScore)) 1112 verifyEvent(t, events.NewValidatorScore(context.Background(), "node19", "1", num.MustDecimalFromString("0.06545454545454544"), num.MustDecimalFromString("0.1469387755102041"), num.MustDecimalFromString("0.0818181818181818"), num.DecimalFromFloat(0.8), num.DecimalFromFloat(1), "ersatz"), bEvents[18].(*events.ValidatorScore)) 1113 verifyEvent(t, events.NewValidatorScore(context.Background(), "node20", "1", num.MustDecimalFromString("0.06545454545454543"), num.MustDecimalFromString("0.1469387755102039"), num.MustDecimalFromString("0.0727272727272727"), num.DecimalFromFloat(0.9), num.DecimalFromFloat(1), "ersatz"), bEvents[19].(*events.ValidatorScore)) 1114 } 1115 1116 func verifyEvent(t *testing.T, expected, actual *events.ValidatorScore) { 1117 t.Helper() 1118 require.Equal(t, expected.EpochSeq, actual.EpochSeq) 1119 require.Equal(t, expected.ValidatorScore, actual.ValidatorScore) 1120 require.Equal(t, expected.MultisigScore, actual.MultisigScore) 1121 require.Equal(t, expected.NodeID, actual.NodeID) 1122 require.Equal(t, expected.NormalisedScore, actual.NormalisedScore) 1123 require.Equal(t, expected.RawValidatorScore, actual.RawValidatorScore) 1124 require.Equal(t, expected.ValidatorPerformance, actual.ValidatorPerformance) 1125 require.Equal(t, expected.ValidatorStatus, actual.ValidatorStatus) 1126 } 1127 1128 func TestAddressMapping(t *testing.T) { 1129 tmKey := "7kRL1jCJH8QUDTHK90/Nz9lIAvl8/s1Z70XL1EXFkaM=" 1130 require.Equal(t, "13fa0b679d6064772567c7a6050b42cca1c7c8cd", tmPubKeyToAddress(tmKey)) 1131 } 1132 1133 type MockPerformanceScore struct{} 1134 1135 func (*MockPerformanceScore) ValidatorPerformanceScore(tmPubKey string, power, totalPower int64, scalingFactor num.Decimal) num.Decimal { 1136 if tmPubKey == "key6" || tmPubKey == "key8" { 1137 return num.DecimalFromFloat(0.3) 1138 } 1139 if tmPubKey == "key7" || tmPubKey == "key9" { 1140 return num.DecimalFromFloat(0.7) 1141 } 1142 if tmPubKey == "key10" { 1143 return num.DecimalFromFloat(1) 1144 } 1145 return num.DecimalFromFloat(0.8) 1146 } 1147 1148 func (*MockPerformanceScore) BeginBlock(ctx context.Context, proposer string) { 1149 } 1150 1151 func (*MockPerformanceScore) Serialize() *v1.ValidatorPerformance { 1152 return nil 1153 } 1154 func (*MockPerformanceScore) Deserialize(*v1.ValidatorPerformance) {} 1155 1156 func (*MockPerformanceScore) Reset() {}