code.vegaprotocol.io/vega@v0.79.0/core/validators/validator_set_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 "time" 23 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/crypto" 26 "code.vegaprotocol.io/vega/libs/num" 27 "code.vegaprotocol.io/vega/logging" 28 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestValidatorSet(t *testing.T) { 33 t.Run("test update of multisig signers network parameter", testUpdateNumberEthMultisigSigners) 34 t.Run("test update of number of validators network parameter", testUpdateNumberOfTendermintValidators) 35 t.Run("test update of incumbent factor network parameter", testUpdateValidatorIncumbentBonusFactor) 36 t.Run("test update of min eth events for new validator network parameter", testUpdateMinimumEthereumEventsForNewValidator) 37 t.Run("test update of minimum required stake", testUpdateMinimumRequireSelfStake) 38 t.Run("test counting of forwarded events for pending validators", testAddForwarder) 39 t.Run("test the number of tendermint validators is reduced hence validators being demoted", testTendermintValidatorsNumberReduced) 40 t.Run("test the number of tendermint validators is greater than the number of current tm, promotion is available", testTendermintFreeSlotsPromotion) 41 t.Run("test swap of the best ersatz with the worst tendermint validators", testSwapBestErsatzWithWorstTendermint) 42 t.Run("test the number of ersatz validators is reduced hence validators being demoted", testErsatzValidatorsNumberReduced) 43 t.Run("test the number of ersatz validators is greater than the number of current tm, promotion is available", testErsatzFreeSlotsPromotion) 44 t.Run("test swap of the best pending with the worst ersatz validators", testSwapBestPendingWithWorstErsatz) 45 t.Run("test swap of from ez to tendermint with slot reduction in ersatz", testSwapAndErsatzSlotDecrease) 46 t.Run("test swap of from ez to tendermint with slot increase in tendermint", testSwapAndTendermintSlotIncrease) 47 } 48 49 func TestDecreaseNumberOfTendermintValidators(t *testing.T) { 50 tm := int64(1654747635) 51 rng := rand.New(rand.NewSource(tm)) 52 byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock } 53 rankingScore := map[string]num.Decimal{ 54 "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.NewDecimalFromFloat(0.6), 55 "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.NewDecimalFromFloat(0.3), 56 "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.NewDecimalFromFloat(0.7), 57 "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.NewDecimalFromFloat(0.2), 58 "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalOne(), 59 } 60 61 topology := &Topology{} 62 topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3)) 63 64 valStates := []*valState{ 65 { 66 data: ValidatorData{ 67 ID: "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908", 68 EthereumAddress: crypto.RandomHash(), 69 }, 70 statusChangeBlock: 1, 71 status: ValidatorStatusTendermint, 72 }, 73 { 74 data: ValidatorData{ 75 ID: "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117", 76 EthereumAddress: crypto.RandomHash(), 77 }, 78 statusChangeBlock: 1, 79 status: ValidatorStatusTendermint, 80 }, 81 { 82 data: ValidatorData{ 83 ID: "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3", 84 EthereumAddress: crypto.RandomHash(), 85 }, 86 statusChangeBlock: 1, 87 status: ValidatorStatusTendermint, 88 }, 89 { 90 data: ValidatorData{ 91 ID: "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066", 92 EthereumAddress: crypto.RandomHash(), 93 }, 94 statusChangeBlock: 1, 95 status: ValidatorStatusTendermint, 96 }, 97 { 98 data: ValidatorData{ 99 ID: "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd", 100 EthereumAddress: crypto.RandomHash(), 101 }, 102 statusChangeBlock: 1, 103 status: ValidatorStatusTendermint, 104 }, 105 } 106 107 // only 4 of the 5 validators are signers on the bridge 108 signers := map[string]struct{}{} 109 for _, vs := range valStates { 110 signers[vs.data.EthereumAddress] = struct{}{} 111 } 112 // 4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd currently having a ranking score of 1 is not a signer 113 delete(signers, valStates[len(valStates)-1].data.EthereumAddress) 114 sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng) 115 116 // effectively can't remove any signer from the bridge and all validators are currently signers 117 threshold := uint32(999) 118 119 tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 120 require.Equal(t, 5, len(tendermintValidators)) 121 require.Equal(t, 0, len(remainingValidators)) 122 require.Equal(t, 0, len(removedFromTM)) 123 124 count := 0 125 for _, valState := range valStates { 126 if valState.status == ValidatorStatusTendermint { 127 count++ 128 } 129 } 130 require.Equal(t, 5, count) 131 132 // let change the ranking score of the validator who is not a signer to be lowest so that it can be removed regardless of the threshold 133 rankingScore["4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd"] = num.DecimalFromFloat(0.1) 134 sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng) 135 136 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 137 require.Equal(t, 4, len(tendermintValidators)) 138 require.Equal(t, 1, len(remainingValidators)) 139 require.Equal(t, 1, len(removedFromTM)) 140 141 require.Equal(t, "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd", removedFromTM[0]) 142 count = 0 143 for _, valState := range valStates { 144 if valState.status == ValidatorStatusTendermint { 145 count++ 146 } 147 } 148 require.Equal(t, 4, count) 149 150 // run for another epoch - non can be decreased anymore with the current threshold 151 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 152 require.Equal(t, 4, len(tendermintValidators)) 153 require.Equal(t, 1, len(remainingValidators)) 154 require.Equal(t, 0, len(removedFromTM)) 155 156 // change the threshold to 700 - expcect one validator to be removed 157 threshold = 700 158 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 159 require.Equal(t, 3, len(tendermintValidators)) 160 require.Equal(t, 2, len(remainingValidators)) 161 require.Equal(t, 1, len(removedFromTM)) 162 163 require.Equal(t, "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066", removedFromTM[0]) 164 count = 0 165 for _, valState := range valStates { 166 if valState.status == ValidatorStatusTendermint { 167 count++ 168 } 169 } 170 require.Equal(t, 3, count) 171 } 172 173 func TestDecreaseNumberOfTendermintValidatorsNotUpdatingContract(t *testing.T) { 174 tm := int64(1654747635) 175 rng := rand.New(rand.NewSource(tm)) 176 byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock } 177 rankingScore := map[string]num.Decimal{ 178 "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.NewDecimalFromFloat(0.6), 179 "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.NewDecimalFromFloat(0.3), 180 "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.NewDecimalFromFloat(0.7), 181 "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.NewDecimalFromFloat(0.2), 182 "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalOne(), 183 } 184 185 topology := &Topology{} 186 topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3)) 187 188 valStates := []*valState{ 189 { 190 data: ValidatorData{ 191 ID: "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908", 192 EthereumAddress: crypto.RandomHash(), 193 }, 194 statusChangeBlock: 1, 195 status: ValidatorStatusTendermint, 196 }, 197 { 198 data: ValidatorData{ 199 ID: "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117", 200 EthereumAddress: crypto.RandomHash(), 201 }, 202 statusChangeBlock: 1, 203 status: ValidatorStatusTendermint, 204 }, 205 { 206 data: ValidatorData{ 207 ID: "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3", 208 EthereumAddress: crypto.RandomHash(), 209 }, 210 statusChangeBlock: 1, 211 status: ValidatorStatusTendermint, 212 }, 213 { 214 data: ValidatorData{ 215 ID: "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066", 216 EthereumAddress: crypto.RandomHash(), 217 }, 218 statusChangeBlock: 1, 219 status: ValidatorStatusTendermint, 220 }, 221 { 222 data: ValidatorData{ 223 ID: "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd", 224 EthereumAddress: crypto.RandomHash(), 225 }, 226 statusChangeBlock: 1, 227 status: ValidatorStatusTendermint, 228 }, 229 } 230 231 signer3Address := valStates[3].data.EthereumAddress 232 233 // starting with 5 signatures on the bridge 234 signers := map[string]struct{}{} 235 for _, vs := range valStates { 236 signers[vs.data.EthereumAddress] = struct{}{} 237 } 238 sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng) 239 240 threshold := uint32(666) 241 242 // one validator is removed 243 tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 244 require.Equal(t, 4, len(tendermintValidators)) 245 require.Equal(t, 1, len(remainingValidators)) 246 require.Equal(t, 1, len(removedFromTM)) 247 248 count := 0 249 for _, valState := range valStates { 250 if valState.status == ValidatorStatusTendermint { 251 count++ 252 } 253 } 254 require.Equal(t, 4, count) 255 256 // we don't update the contract so it still has 5 signers - meaning it should not allow us to remove another validator now 257 rankingScore["4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd"] = num.DecimalFromFloat(0.1) 258 sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng) 259 260 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 261 require.Equal(t, 4, len(tendermintValidators)) 262 require.Equal(t, 1, len(remainingValidators)) 263 require.Equal(t, 0, len(removedFromTM)) 264 265 // now update the contract to remove the signer 266 delete(signers, signer3Address) 267 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold) 268 require.Equal(t, 3, len(tendermintValidators)) 269 require.Equal(t, 2, len(remainingValidators)) 270 require.Equal(t, 1, len(removedFromTM)) 271 } 272 273 func TestApplyPromotionAllThingsEqual(t *testing.T) { 274 tm := int64(1654747635) 275 for i := 0; i < 100; i++ { 276 rng := rand.New(rand.NewSource(tm)) 277 byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock } 278 rankingScore := map[string]num.Decimal{ 279 "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.DecimalZero(), 280 "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.DecimalZero(), 281 "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.DecimalZero(), 282 "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.DecimalZero(), 283 "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalZero(), 284 } 285 286 topology := &Topology{} 287 topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3)) 288 289 valStates := []*valState{ 290 { 291 data: ValidatorData{ 292 ID: "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908", 293 EthereumAddress: crypto.RandomHash(), 294 }, 295 statusChangeBlock: 1, 296 status: ValidatorStatusTendermint, 297 }, 298 { 299 data: ValidatorData{ 300 ID: "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117", 301 EthereumAddress: crypto.RandomHash(), 302 }, 303 statusChangeBlock: 1, 304 status: ValidatorStatusTendermint, 305 }, 306 { 307 data: ValidatorData{ 308 ID: "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3", 309 EthereumAddress: crypto.RandomHash(), 310 }, 311 statusChangeBlock: 1, 312 status: ValidatorStatusTendermint, 313 }, 314 { 315 data: ValidatorData{ 316 ID: "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066", 317 EthereumAddress: crypto.RandomHash(), 318 }, 319 statusChangeBlock: 1, 320 status: ValidatorStatusTendermint, 321 }, 322 { 323 data: ValidatorData{ 324 ID: "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd", 325 EthereumAddress: crypto.RandomHash(), 326 }, 327 statusChangeBlock: 1, 328 status: ValidatorStatusTendermint, 329 }, 330 } 331 332 signer1Address := valStates[1].data.EthereumAddress 333 334 rand.Seed(time.Now().UnixNano()) 335 rand.Shuffle(len(valStates), func(i, j int) { valStates[i], valStates[j] = valStates[j], valStates[i] }) 336 337 sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng) 338 339 signers := map[string]struct{}{} 340 for _, vs := range valStates { 341 signers[vs.data.EthereumAddress] = struct{}{} 342 } 343 344 tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, 666) 345 require.Equal(t, 4, len(tendermintValidators)) 346 require.Equal(t, 1, len(remainingValidators)) 347 require.Equal(t, 1, len(removedFromTM)) 348 349 require.Equal(t, "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117", removedFromTM[0]) 350 count := 0 351 for _, valState := range valStates { 352 if valState.status == ValidatorStatusTendermint { 353 count++ 354 } 355 } 356 require.Equal(t, 4, count) 357 358 delete(signers, signer1Address) 359 360 tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, 666) 361 require.Equal(t, 3, len(tendermintValidators)) 362 require.Equal(t, 1, len(remainingValidators)) 363 require.Equal(t, 1, len(removedFromTM)) 364 365 require.Equal(t, "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908", removedFromTM[0]) 366 count = 0 367 for _, valState := range valStates { 368 if valState.status == ValidatorStatusTendermint { 369 count++ 370 } 371 } 372 require.Equal(t, 3, count) 373 } 374 } 375 376 func TestSortValidatorDescRankingScoreAscBlockStatusChanged(t *testing.T) { 377 tm := int64(1654747635) 378 for i := 0; i < 100; i++ { 379 rng := rand.New(rand.NewSource(tm)) 380 byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock } 381 valStates1 := []*valState{ 382 { 383 data: ValidatorData{ 384 ID: "node1", 385 VegaPubKey: "node1Key", 386 }, 387 statusChangeBlock: 1, 388 }, 389 { 390 data: ValidatorData{ 391 ID: "node2", 392 VegaPubKey: "node2Key", 393 }, 394 statusChangeBlock: 2, 395 }, 396 { 397 data: ValidatorData{ 398 ID: "node3", 399 VegaPubKey: "node3Key", 400 }, 401 statusChangeBlock: 3, 402 }, 403 } 404 rankingScore1 := map[string]num.Decimal{ 405 "node1": num.DecimalFromFloat(0.9), 406 "node2": num.DecimalFromFloat(0.5), 407 "node3": num.DecimalFromFloat(0.7), 408 } 409 410 // can sort simply by ranking score descending 411 sortValidatorDescRankingScoreAscBlockcompare(valStates1, rankingScore1, byStatusChangeBlock, rng) 412 require.Equal(t, "node1", valStates1[0].data.ID) 413 require.Equal(t, "node3", valStates1[1].data.ID) 414 require.Equal(t, "node2", valStates1[2].data.ID) 415 416 valStates2 := []*valState{ 417 { 418 data: ValidatorData{ 419 ID: "node1", 420 VegaPubKey: "node1Key", 421 }, 422 statusChangeBlock: 1, 423 }, 424 { 425 data: ValidatorData{ 426 ID: "node2", 427 VegaPubKey: "node2Key", 428 }, 429 statusChangeBlock: 2, 430 }, 431 { 432 data: ValidatorData{ 433 ID: "node3", 434 VegaPubKey: "node3Key", 435 }, 436 statusChangeBlock: 3, 437 }, 438 } 439 rankingScore2 := map[string]num.Decimal{ 440 "node1": num.DecimalFromFloat(0.9), 441 "node2": num.DecimalFromFloat(0.5), 442 "node3": num.DecimalFromFloat(0.5), 443 } 444 445 // need to use last block change state sorted ascending as tie breaker - node 2 changed state before node 3 so it comes before it 446 sortValidatorDescRankingScoreAscBlockcompare(valStates2, rankingScore2, byStatusChangeBlock, rng) 447 require.Equal(t, "node1", valStates2[0].data.ID) 448 require.Equal(t, "node2", valStates2[1].data.ID) 449 require.Equal(t, "node3", valStates2[2].data.ID) 450 451 valStates3 := []*valState{ 452 { 453 data: ValidatorData{ 454 ID: "node1", 455 VegaPubKey: "node1Key", 456 }, 457 statusChangeBlock: 1, 458 }, 459 { 460 data: ValidatorData{ 461 ID: "node2", 462 VegaPubKey: "node2Key", 463 }, 464 statusChangeBlock: 1, 465 }, 466 { 467 data: ValidatorData{ 468 ID: "node3", 469 VegaPubKey: "node3Key", 470 }, 471 statusChangeBlock: 1, 472 }, 473 { 474 data: ValidatorData{ 475 ID: "node4", 476 VegaPubKey: "node4Key", 477 }, 478 statusChangeBlock: 1, 479 }, 480 } 481 rankingScore3 := map[string]num.Decimal{ 482 "node1": num.DecimalFromFloat(0.9), 483 "node2": num.DecimalFromFloat(0.5), 484 "node3": num.DecimalFromFloat(0.5), 485 "node4": num.DecimalFromFloat(0.9), 486 } 487 488 // need to use last block change state sorted ascending as tie breaker - node 2 changed state before node 3 so it comes before it 489 sortValidatorDescRankingScoreAscBlockcompare(valStates3, rankingScore3, byStatusChangeBlock, rng) 490 require.Equal(t, "node1", valStates3[0].data.ID) 491 require.Equal(t, "node4", valStates3[1].data.ID) 492 require.Equal(t, "node2", valStates3[2].data.ID) 493 require.Equal(t, "node3", valStates3[3].data.ID) 494 495 valStates4 := []*valState{ 496 { 497 data: ValidatorData{ 498 ID: "node1", 499 VegaPubKey: "node1Key", 500 }, 501 statusChangeBlock: 1, 502 }, 503 { 504 data: ValidatorData{ 505 ID: "node2", 506 VegaPubKey: "node2Key", 507 }, 508 statusChangeBlock: 1, 509 }, 510 { 511 data: ValidatorData{ 512 ID: "node3", 513 VegaPubKey: "node3Key", 514 }, 515 statusChangeBlock: 1, 516 }, 517 { 518 data: ValidatorData{ 519 ID: "node4", 520 VegaPubKey: "node4Key", 521 }, 522 statusChangeBlock: 1, 523 }, 524 } 525 rankingScore4 := map[string]num.Decimal{ 526 "node1": num.DecimalFromFloat(0.5), 527 "node2": num.DecimalFromFloat(0.5), 528 "node3": num.DecimalFromFloat(0.5), 529 "node4": num.DecimalFromFloat(0.9), 530 } 531 sortValidatorDescRankingScoreAscBlockcompare(valStates4, rankingScore4, byStatusChangeBlock, rng) 532 require.Equal(t, "node4", valStates4[0].data.ID) 533 require.Equal(t, "node1", valStates4[1].data.ID) 534 require.Equal(t, "node2", valStates4[2].data.ID) 535 require.Equal(t, "node3", valStates4[3].data.ID) 536 } 537 } 538 539 func testUpdateNumberEthMultisigSigners(t *testing.T) { 540 topology := &Topology{} 541 topology.UpdateNumberEthMultisigSigners(context.Background(), num.NewUint(10)) 542 require.Equal(t, 10, topology.numberEthMultisigSigners) 543 } 544 545 func testUpdateNumberOfTendermintValidators(t *testing.T) { 546 topology := &Topology{} 547 topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(20)) 548 topology.UpdateErsatzValidatorsFactor(context.Background(), num.DecimalFromFloat(0.5)) 549 require.Equal(t, 20, topology.numberOfTendermintValidators) 550 require.Equal(t, 10, topology.numberOfErsatzValidators) 551 } 552 553 func testUpdateValidatorIncumbentBonusFactor(t *testing.T) { 554 topology := &Topology{} 555 topology.UpdateValidatorIncumbentBonusFactor(context.Background(), num.DecimalFromFloat(0.2)) 556 require.Equal(t, "1.2", topology.validatorIncumbentBonusFactor.String()) 557 } 558 559 func testUpdateMinimumEthereumEventsForNewValidator(t *testing.T) { 560 topology := &Topology{} 561 topology.UpdateMinimumEthereumEventsForNewValidator(context.Background(), num.NewUint(4)) 562 require.Equal(t, uint64(4), topology.minimumEthereumEventsForNewValidator) 563 } 564 565 func testUpdateMinimumRequireSelfStake(t *testing.T) { 566 topology := &Topology{} 567 topology.UpdateMinimumRequireSelfStake(context.Background(), num.DecimalFromFloat(30000)) 568 require.Equal(t, num.NewUint(30000), topology.minimumStake) 569 } 570 571 func testAddForwarder(t *testing.T) { 572 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 573 // add unknown forwarder 574 topology.AddForwarder("node1") 575 require.Equal(t, 0, len(topology.validators)) 576 577 topology.validators["node2"] = &valState{ 578 data: ValidatorData{ 579 ID: "node2", 580 VegaPubKey: "node2Key", 581 }, 582 heartbeatTracker: &validatorHeartbeatTracker{}, 583 } 584 topology.validators["node3"] = &valState{ 585 data: ValidatorData{ 586 ID: "node3", 587 VegaPubKey: "node3Key", 588 }, 589 heartbeatTracker: &validatorHeartbeatTracker{}, 590 } 591 require.Equal(t, uint64(0), topology.validators["node3"].numberOfEthereumEventsForwarded) 592 require.Equal(t, uint64(0), topology.validators["node2"].numberOfEthereumEventsForwarded) 593 topology.AddForwarder("node3Key") 594 require.Equal(t, uint64(1), topology.validators["node3"].numberOfEthereumEventsForwarded) 595 require.Equal(t, uint64(0), topology.validators["node2"].numberOfEthereumEventsForwarded) 596 597 topology.AddForwarder("node2Key") 598 require.Equal(t, uint64(1), topology.validators["node3"].numberOfEthereumEventsForwarded) 599 require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded) 600 topology.AddForwarder("node3Key") 601 require.Equal(t, uint64(2), topology.validators["node3"].numberOfEthereumEventsForwarded) 602 require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded) 603 topology.AddForwarder("node3Key") 604 require.Equal(t, uint64(3), topology.validators["node3"].numberOfEthereumEventsForwarded) 605 require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded) 606 } 607 608 func testTendermintValidatorsNumberReduced(t *testing.T) { 609 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 610 topology.numberOfTendermintValidators = 5 611 topology.rng = rand.New(rand.NewSource(100000)) 612 topology.validators["node1"] = &valState{ 613 data: ValidatorData{ 614 ID: "node1", 615 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 616 }, 617 blockAdded: 1, 618 status: ValidatorStatusTendermint, 619 heartbeatTracker: &validatorHeartbeatTracker{}, 620 } 621 topology.validators["node2"] = &valState{ 622 data: ValidatorData{ 623 ID: "node2", 624 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 625 }, 626 blockAdded: 1, 627 status: ValidatorStatusTendermint, 628 heartbeatTracker: &validatorHeartbeatTracker{}, 629 } 630 topology.validators["node3"] = &valState{ 631 data: ValidatorData{ 632 ID: "node3", 633 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 634 }, 635 blockAdded: 2, 636 status: ValidatorStatusTendermint, 637 heartbeatTracker: &validatorHeartbeatTracker{}, 638 } 639 topology.validators["node4"] = &valState{ 640 data: ValidatorData{ 641 ID: "node4", 642 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 643 }, 644 blockAdded: 3, 645 status: ValidatorStatusTendermint, 646 heartbeatTracker: &validatorHeartbeatTracker{}, 647 } 648 topology.validators["node5"] = &valState{ 649 data: ValidatorData{ 650 ID: "node5", 651 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 652 }, 653 blockAdded: 4, 654 status: ValidatorStatusTendermint, 655 heartbeatTracker: &validatorHeartbeatTracker{}, 656 } 657 658 perf := map[string]num.Decimal{ 659 "node1": decimalOne, 660 "node2": decimalOne, 661 "node3": decimalOne, 662 "node4": decimalOne, 663 "node5": decimalOne, 664 } 665 666 ranking := map[string]num.Decimal{ 667 "node1": num.DecimalFromFloat(0.8), 668 "node2": num.DecimalFromFloat(0.8), 669 "node3": num.DecimalFromFloat(0.4), 670 "node4": num.DecimalFromFloat(0.5), 671 "node5": num.DecimalFromFloat(0.6), 672 } 673 674 delegations := []*types.ValidatorData{ 675 {NodeID: "node1", SelfStake: num.NewUint(3000), StakeByDelegators: num.NewUint(7000)}, 676 {NodeID: "node2", SelfStake: num.NewUint(3000), StakeByDelegators: num.NewUint(2000)}, 677 {NodeID: "node5", SelfStake: num.NewUint(85000), StakeByDelegators: num.NewUint(0)}, 678 } 679 680 // reduce the number of tendermint validators to 3 to that 2 must be removed, i.e. nodes 3 and 4 with the lowest scores 681 topology.currentBlockHeight = 1000 682 topology.numberOfTendermintValidators = 3 683 topology.numberOfErsatzValidators = 5 684 res, _ := topology.applyPromotion(perf, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(3), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)}) 685 686 // node1 has 10000 / 100000 = 0.1 => 0.6666666667 => 6666 687 require.Equal(t, int64(6666), res[0].Power) // 10000 * 0.8/2.2 688 // node2 has 5000 / 100000 = 0.05 => 0.3333333333 => 3333 689 require.Equal(t, int64(3333), res[1].Power) // 10000 * 0.8/2.2 690 // node3 is remove => 0 691 require.Equal(t, int64(0), res[2].Power) // remove from rm 692 // node4 is remove => 0 693 require.Equal(t, int64(0), res[3].Power) // remove from rm 694 // node5 is anti-whaled => 0 => 0 => 10 695 require.Equal(t, int64(1), res[4].Power) // 10000 * 0.6/2.2 696 697 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status]) 698 require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock) 699 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node4"].status]) 700 require.Equal(t, int64(1001), topology.validators["node4"].statusChangeBlock) 701 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 702 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status]) 703 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status]) 704 } 705 706 func testTendermintFreeSlotsPromotion(t *testing.T) { 707 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 708 topology.numberOfTendermintValidators = 5 709 topology.numberOfErsatzValidators = 1 710 topology.validators["node1"] = &valState{ 711 data: ValidatorData{ 712 ID: "node1", 713 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 714 }, 715 blockAdded: 1, 716 status: ValidatorStatusTendermint, 717 statusChangeBlock: 900, 718 heartbeatTracker: &validatorHeartbeatTracker{}, 719 } 720 topology.validators["node2"] = &valState{ 721 data: ValidatorData{ 722 ID: "node2", 723 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 724 }, 725 blockAdded: 1, 726 status: ValidatorStatusTendermint, 727 statusChangeBlock: 901, 728 heartbeatTracker: &validatorHeartbeatTracker{}, 729 } 730 topology.validators["node3"] = &valState{ 731 data: ValidatorData{ 732 ID: "node3", 733 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 734 }, 735 blockAdded: 2, 736 status: ValidatorStatusTendermint, 737 statusChangeBlock: 902, 738 heartbeatTracker: &validatorHeartbeatTracker{}, 739 } 740 topology.validators["node4"] = &valState{ 741 data: ValidatorData{ 742 ID: "node4", 743 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 744 }, 745 blockAdded: 3, 746 status: ValidatorStatusPending, 747 statusChangeBlock: 903, 748 numberOfEthereumEventsForwarded: 2, 749 heartbeatTracker: &validatorHeartbeatTracker{ 750 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 751 }, 752 } 753 topology.validators["node5"] = &valState{ 754 data: ValidatorData{ 755 ID: "node5", 756 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 757 }, 758 blockAdded: 4, 759 statusChangeBlock: 904, 760 status: ValidatorStatusErsatz, 761 numberOfEthereumEventsForwarded: 4, 762 heartbeatTracker: &validatorHeartbeatTracker{ 763 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 764 }, 765 } 766 767 perfScore := map[string]num.Decimal{ 768 "node1": decimalOne, 769 "node2": decimalOne, 770 "node3": decimalOne, 771 "node4": decimalOne, 772 "node5": decimalOne, 773 } 774 775 ranking := map[string]num.Decimal{ 776 "node1": num.DecimalFromFloat(0.8), 777 "node2": num.DecimalFromFloat(0.8), 778 "node3": num.DecimalFromFloat(0.4), 779 "node4": num.DecimalFromFloat(0), 780 "node5": num.DecimalFromFloat(0.6), 781 } 782 783 delegations := []*types.ValidatorData{ 784 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)}, 785 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)}, 786 {NodeID: "node3", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)}, 787 {NodeID: "node4", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 788 {NodeID: "node5", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)}, 789 } 790 791 // there are 5 slots for tm validators but only 3 currently 792 // there are 2 potential validators for promotion but one of them has not completed their prereq 793 topology.currentBlockHeight = 1000 794 res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(4), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)}) 795 require.Equal(t, 4, len(res)) 796 // node1 => 20000 / 100000 => 0.2 => 0.2857142857 => 3333 797 require.Equal(t, int64(2857), res[0].Power) 798 // node2 => 15000 / 100000 => 0.15 => 0.2142857143 => 2142 799 require.Equal(t, int64(2142), res[1].Power) 800 // node3 => 25000 / 100000 => 0.25 => 0.3571428571 = 3571 801 require.Equal(t, int64(3571), res[2].Power) 802 // node5 => 40000 / 100000 => 0.1 (0.4 - 0.15 - 0.15)=> 0.1428571429 => 1428 803 require.Equal(t, int64(1428), res[3].Power) 804 805 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 806 require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock) 807 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status]) 808 require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock) 809 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node3"].status]) 810 require.Equal(t, int64(902), topology.validators["node3"].statusChangeBlock) 811 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status]) 812 require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock) 813 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status]) 814 require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock) 815 } 816 817 func testSwapBestErsatzWithWorstTendermint(t *testing.T) { 818 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 819 topology.numberOfTendermintValidators = 4 820 topology.numberOfErsatzValidators = 1 821 topology.validators["node1"] = &valState{ 822 data: ValidatorData{ 823 ID: "node1", 824 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 825 }, 826 blockAdded: 1, 827 status: ValidatorStatusTendermint, 828 statusChangeBlock: 900, 829 heartbeatTracker: &validatorHeartbeatTracker{}, 830 } 831 topology.validators["node2"] = &valState{ 832 data: ValidatorData{ 833 ID: "node2", 834 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 835 }, 836 blockAdded: 1, 837 status: ValidatorStatusTendermint, 838 statusChangeBlock: 901, 839 heartbeatTracker: &validatorHeartbeatTracker{}, 840 } 841 topology.validators["node3"] = &valState{ 842 data: ValidatorData{ 843 ID: "node3", 844 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 845 }, 846 blockAdded: 2, 847 status: ValidatorStatusTendermint, 848 statusChangeBlock: 902, 849 heartbeatTracker: &validatorHeartbeatTracker{}, 850 } 851 topology.validators["node4"] = &valState{ 852 data: ValidatorData{ 853 ID: "node4", 854 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 855 }, 856 blockAdded: 3, 857 status: ValidatorStatusTendermint, 858 statusChangeBlock: 903, 859 heartbeatTracker: &validatorHeartbeatTracker{}, 860 } 861 topology.validators["node5"] = &valState{ 862 data: ValidatorData{ 863 ID: "node5", 864 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 865 }, 866 blockAdded: 4, 867 statusChangeBlock: 904, 868 status: ValidatorStatusErsatz, 869 heartbeatTracker: &validatorHeartbeatTracker{}, 870 } 871 872 perfScore := map[string]num.Decimal{ 873 "node1": decimalOne, 874 "node2": decimalOne, 875 "node3": decimalOne, 876 "node4": decimalOne, 877 "node5": decimalOne, 878 } 879 880 ranking := map[string]num.Decimal{ 881 "node1": num.DecimalFromFloat(0.8), 882 "node2": num.DecimalFromFloat(0.8), 883 "node3": num.DecimalFromFloat(0.4), 884 "node4": num.DecimalFromFloat(0.5), 885 "node5": num.DecimalFromFloat(0.6), 886 } 887 888 delegations := []*types.ValidatorData{ 889 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)}, 890 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)}, 891 {NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)}, 892 {NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)}, 893 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 894 } 895 896 // there are 4 slots for tm validators and the best ersatz (node5) has better performance than the worst tm (node3) 897 // therefore node3 is kicked out of tm and becomes ersatz and node5 is added to tm 898 topology.currentBlockHeight = 1000 899 res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(4), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)}) 900 require.Equal(t, 5, len(res)) 901 // node1 => 20000 / 100000 => 0.2 => 0.2857142857 => 3333 902 require.Equal(t, int64(2857), res[0].Power) 903 // node2 => 15000 / 100000 => 0.15 => 0.2142857143 => 2142 904 require.Equal(t, int64(2142), res[1].Power) 905 require.Equal(t, int64(0), res[2].Power) // node3 kicked out of tm 906 // node4 => 25000 / 100000 => 0.25 => 0.3571428571 = 3571 907 require.Equal(t, int64(3571), res[3].Power) 908 // node5 => 40000 / 100000 => 0.1 (0.4 - 0.15 - 0.15)=> 0.1428571429 => 1428 909 require.Equal(t, int64(1428), res[4].Power) 910 911 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 912 require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock) 913 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status]) 914 require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock) 915 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status]) 916 require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock) 917 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node4"].status]) 918 require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock) 919 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status]) 920 require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock) 921 } 922 923 func testErsatzFreeSlotsPromotion(t *testing.T) { 924 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 925 topology.numberOfTendermintValidators = 1 926 topology.numberOfErsatzValidators = 4 927 928 topology.validators["node1"] = &valState{ 929 data: ValidatorData{ 930 ID: "node1", 931 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 932 }, 933 blockAdded: 1, 934 status: ValidatorStatusTendermint, 935 statusChangeBlock: 900, 936 heartbeatTracker: &validatorHeartbeatTracker{}, 937 } 938 topology.validators["node2"] = &valState{ 939 data: ValidatorData{ 940 ID: "node2", 941 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 942 }, 943 blockAdded: 1, 944 status: ValidatorStatusErsatz, 945 statusChangeBlock: 901, 946 heartbeatTracker: &validatorHeartbeatTracker{}, 947 } 948 topology.validators["node3"] = &valState{ 949 data: ValidatorData{ 950 ID: "node3", 951 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 952 }, 953 blockAdded: 2, 954 status: ValidatorStatusErsatz, 955 statusChangeBlock: 902, 956 heartbeatTracker: &validatorHeartbeatTracker{}, 957 } 958 topology.validators["node4"] = &valState{ 959 data: ValidatorData{ 960 ID: "node4", 961 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 962 }, 963 blockAdded: 3, 964 status: ValidatorStatusPending, 965 statusChangeBlock: 903, 966 numberOfEthereumEventsForwarded: 2, 967 heartbeatTracker: &validatorHeartbeatTracker{ 968 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 969 }, 970 } 971 topology.validators["node5"] = &valState{ 972 data: ValidatorData{ 973 ID: "node5", 974 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 975 }, 976 blockAdded: 4, 977 statusChangeBlock: 904, 978 status: ValidatorStatusPending, 979 numberOfEthereumEventsForwarded: 4, 980 heartbeatTracker: &validatorHeartbeatTracker{ 981 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 982 }, 983 } 984 985 perfScore := map[string]num.Decimal{ 986 "node1": decimalOne, 987 "node2": decimalOne, 988 "node3": decimalOne, 989 "node4": decimalOne, 990 "node5": decimalOne, 991 } 992 993 ranking := map[string]num.Decimal{ 994 "node1": num.DecimalFromFloat(0.8), 995 "node2": num.DecimalFromFloat(0.8), 996 "node3": num.DecimalFromFloat(0.4), 997 "node4": num.DecimalFromFloat(0), 998 "node5": num.DecimalFromFloat(0.6), 999 } 1000 1001 delegations := []*types.ValidatorData{ 1002 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)}, 1003 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)}, 1004 {NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)}, 1005 {NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)}, 1006 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1007 } 1008 1009 // there's only 1 slot for tendermint and it's taken by the node with the highest rank anyways. 1010 // there are 4 slots for ersatz validators and only 2 taken so the other two can be promoted 1011 // there are 2 potential validators for promotion but one of them has not completed their prerequisites 1012 topology.currentBlockHeight = 1000 1013 res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(3)}) 1014 require.Equal(t, int64(10000), res[0].Power) // only node 1 is here 1015 1016 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 1017 require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock) 1018 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status]) 1019 require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock) 1020 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status]) 1021 require.Equal(t, int64(902), topology.validators["node3"].statusChangeBlock) 1022 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status]) 1023 require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock) 1024 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node5"].status]) 1025 require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock) 1026 } 1027 1028 func testSwapBestPendingWithWorstErsatz(t *testing.T) { 1029 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 1030 topology.numberOfTendermintValidators = 1 1031 topology.numberOfErsatzValidators = 2 1032 1033 topology.validators["node1"] = &valState{ 1034 data: ValidatorData{ 1035 ID: "node1", 1036 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 1037 }, 1038 blockAdded: 1, 1039 status: ValidatorStatusTendermint, 1040 statusChangeBlock: 900, 1041 heartbeatTracker: &validatorHeartbeatTracker{}, 1042 } 1043 topology.validators["node2"] = &valState{ 1044 data: ValidatorData{ 1045 ID: "node2", 1046 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 1047 }, 1048 blockAdded: 1, 1049 status: ValidatorStatusErsatz, 1050 statusChangeBlock: 901, 1051 heartbeatTracker: &validatorHeartbeatTracker{}, 1052 } 1053 topology.validators["node3"] = &valState{ 1054 data: ValidatorData{ 1055 ID: "node3", 1056 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 1057 }, 1058 blockAdded: 2, 1059 status: ValidatorStatusErsatz, 1060 statusChangeBlock: 902, 1061 heartbeatTracker: &validatorHeartbeatTracker{}, 1062 } 1063 topology.validators["node4"] = &valState{ 1064 data: ValidatorData{ 1065 ID: "node4", 1066 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 1067 }, 1068 blockAdded: 3, 1069 status: ValidatorStatusPending, 1070 statusChangeBlock: 903, 1071 numberOfEthereumEventsForwarded: 2, 1072 heartbeatTracker: &validatorHeartbeatTracker{ 1073 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 1074 }, 1075 } 1076 topology.validators["node5"] = &valState{ 1077 data: ValidatorData{ 1078 ID: "node5", 1079 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1080 }, 1081 blockAdded: 4, 1082 statusChangeBlock: 904, 1083 status: ValidatorStatusPending, 1084 numberOfEthereumEventsForwarded: 4, 1085 heartbeatTracker: &validatorHeartbeatTracker{ 1086 blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false}, 1087 }, 1088 } 1089 1090 perfScore := map[string]num.Decimal{ 1091 "node1": decimalOne, 1092 "node2": decimalOne, 1093 "node3": decimalOne, 1094 "node4": decimalOne, 1095 "node5": decimalOne, 1096 } 1097 1098 ranking := map[string]num.Decimal{ 1099 "node1": num.DecimalFromFloat(0.8), 1100 "node2": num.DecimalFromFloat(0.8), 1101 "node3": num.DecimalFromFloat(0.4), 1102 "node4": num.DecimalFromFloat(0), 1103 "node5": num.DecimalFromFloat(0.6), 1104 } 1105 1106 delegations := []*types.ValidatorData{ 1107 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)}, 1108 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)}, 1109 {NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)}, 1110 {NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)}, 1111 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1112 } 1113 1114 // there's only 1 slot for tendermint and it's taken by the node with the highest rank anyways. 1115 // there are 2 slots for ersatz validators both taken 1116 // the score of node5 is higher than the lowest ersatz so they swap places 1117 topology.currentBlockHeight = 1000 1118 res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)}) 1119 require.Equal(t, int64(10000), res[0].Power) // only node 1 is here 1120 1121 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 1122 require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock) 1123 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status]) 1124 require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock) 1125 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node3"].status]) 1126 require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock) 1127 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status]) 1128 require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock) 1129 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node5"].status]) 1130 require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock) 1131 } 1132 1133 func testErsatzValidatorsNumberReduced(t *testing.T) { 1134 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 1135 topology.numberOfTendermintValidators = 1 1136 topology.validators["node1"] = &valState{ 1137 data: ValidatorData{ 1138 ID: "node1", 1139 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 1140 }, 1141 blockAdded: 1, 1142 statusChangeBlock: 900, 1143 status: ValidatorStatusTendermint, 1144 heartbeatTracker: &validatorHeartbeatTracker{}, 1145 } 1146 topology.validators["node2"] = &valState{ 1147 data: ValidatorData{ 1148 ID: "node2", 1149 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 1150 }, 1151 blockAdded: 1, 1152 statusChangeBlock: 901, 1153 status: ValidatorStatusErsatz, 1154 heartbeatTracker: &validatorHeartbeatTracker{}, 1155 } 1156 topology.validators["node3"] = &valState{ 1157 data: ValidatorData{ 1158 ID: "node3", 1159 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 1160 }, 1161 blockAdded: 2, 1162 status: ValidatorStatusErsatz, 1163 heartbeatTracker: &validatorHeartbeatTracker{}, 1164 } 1165 topology.validators["node4"] = &valState{ 1166 data: ValidatorData{ 1167 ID: "node4", 1168 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 1169 }, 1170 blockAdded: 3, 1171 status: ValidatorStatusErsatz, 1172 heartbeatTracker: &validatorHeartbeatTracker{}, 1173 } 1174 topology.validators["node5"] = &valState{ 1175 data: ValidatorData{ 1176 ID: "node5", 1177 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1178 }, 1179 blockAdded: 4, 1180 status: ValidatorStatusErsatz, 1181 heartbeatTracker: &validatorHeartbeatTracker{}, 1182 } 1183 1184 perfScore := map[string]num.Decimal{ 1185 "node1": decimalOne, 1186 "node2": decimalOne, 1187 "node3": decimalOne, 1188 "node4": decimalOne, 1189 "node5": decimalOne, 1190 } 1191 1192 ranking := map[string]num.Decimal{ 1193 "node1": num.DecimalFromFloat(0.8), 1194 "node2": num.DecimalFromFloat(0.8), 1195 "node3": num.DecimalFromFloat(0.4), 1196 "node4": num.DecimalFromFloat(0.5), 1197 "node5": num.DecimalFromFloat(0.6), 1198 } 1199 1200 delegations := []*types.ValidatorData{ 1201 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)}, 1202 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)}, 1203 {NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)}, 1204 {NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)}, 1205 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1206 } 1207 1208 // reduce the number of ersatz validators from 4 to 1 so that 3 with the lower scores are demoted to pending 1209 topology.currentBlockHeight = 1000 1210 topology.numberOfErsatzValidators = 1 1211 res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)}) 1212 require.Equal(t, int64(10000), res[0].Power) // 10000 * 1 1213 1214 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status]) 1215 require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock) 1216 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status]) 1217 require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock) 1218 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node3"].status]) 1219 require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock) 1220 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status]) 1221 require.Equal(t, int64(1001), topology.validators["node4"].statusChangeBlock) 1222 require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node5"].status]) 1223 require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock) 1224 } 1225 1226 func testSwapAndErsatzSlotDecrease(t *testing.T) { 1227 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 1228 topology.numberOfTendermintValidators = 4 1229 topology.numberOfErsatzValidators = 2 1230 topology.validators["node1"] = &valState{ 1231 data: ValidatorData{ 1232 ID: "node1", 1233 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 1234 }, 1235 blockAdded: 1, 1236 statusChangeBlock: 900, 1237 status: ValidatorStatusTendermint, 1238 heartbeatTracker: &validatorHeartbeatTracker{}, 1239 } 1240 topology.validators["node2"] = &valState{ 1241 data: ValidatorData{ 1242 ID: "node2", 1243 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 1244 }, 1245 blockAdded: 1, 1246 statusChangeBlock: 901, 1247 status: ValidatorStatusTendermint, 1248 heartbeatTracker: &validatorHeartbeatTracker{}, 1249 } 1250 topology.validators["node3"] = &valState{ 1251 data: ValidatorData{ 1252 ID: "node3", 1253 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 1254 }, 1255 blockAdded: 2, 1256 status: ValidatorStatusTendermint, 1257 heartbeatTracker: &validatorHeartbeatTracker{}, 1258 } 1259 topology.validators["node4"] = &valState{ 1260 data: ValidatorData{ 1261 ID: "node4", 1262 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 1263 }, 1264 blockAdded: 3, 1265 status: ValidatorStatusTendermint, 1266 heartbeatTracker: &validatorHeartbeatTracker{}, 1267 } 1268 topology.validators["node5"] = &valState{ 1269 data: ValidatorData{ 1270 ID: "node5", 1271 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1272 }, 1273 blockAdded: 4, 1274 status: ValidatorStatusErsatz, 1275 heartbeatTracker: &validatorHeartbeatTracker{}, 1276 } 1277 topology.validators["node6"] = &valState{ 1278 data: ValidatorData{ 1279 ID: "node5", 1280 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1281 }, 1282 blockAdded: 4, 1283 status: ValidatorStatusErsatz, 1284 heartbeatTracker: &validatorHeartbeatTracker{}, 1285 } 1286 1287 perfScore := map[string]num.Decimal{ 1288 "node1": decimalOne, 1289 "node2": decimalOne, 1290 "node3": decimalOne, 1291 "node4": decimalOne, 1292 "node5": decimalOne, 1293 "node6": decimalOne, 1294 } 1295 1296 ranking := map[string]num.Decimal{ 1297 "node1": num.DecimalFromFloat(0.1), 1298 "node2": num.DecimalFromFloat(0.8), 1299 "node3": num.DecimalFromFloat(0.8), 1300 "node4": num.DecimalFromFloat(0.8), 1301 "node5": num.DecimalFromFloat(0.8), 1302 "node6": num.DecimalFromFloat(0.5), 1303 } 1304 1305 delegations := []*types.ValidatorData{ 1306 {NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(10000)}, 1307 {NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(10000)}, 1308 {NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(10000)}, 1309 {NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(10000)}, 1310 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1311 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1312 {NodeID: "node6", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1313 } 1314 topology.rng = rand.New(rand.NewSource(1000)) 1315 // reduce the number of ersatz validators from 2 to 1 1316 topology.currentBlockHeight = 1000 1317 topology.numberOfErsatzValidators = 1 1318 topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)}) 1319 1320 ezCount := 0 1321 for _, v := range topology.validators { 1322 if ValidatorStatusToName[v.status] == "ersatz" { 1323 ezCount++ 1324 } 1325 } 1326 1327 require.Equal(t, ezCount, topology.numberOfErsatzValidators) 1328 } 1329 1330 func testSwapAndTendermintSlotIncrease(t *testing.T) { 1331 topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{}) 1332 topology.numberOfTendermintValidators = 4 1333 topology.numberOfErsatzValidators = 2 1334 topology.validators["node1"] = &valState{ 1335 data: ValidatorData{ 1336 ID: "node1", 1337 TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=", 1338 }, 1339 blockAdded: 1, 1340 statusChangeBlock: 900, 1341 status: ValidatorStatusTendermint, 1342 heartbeatTracker: &validatorHeartbeatTracker{}, 1343 } 1344 topology.validators["node2"] = &valState{ 1345 data: ValidatorData{ 1346 ID: "node2", 1347 TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=", 1348 }, 1349 blockAdded: 1, 1350 statusChangeBlock: 901, 1351 status: ValidatorStatusTendermint, 1352 heartbeatTracker: &validatorHeartbeatTracker{}, 1353 } 1354 topology.validators["node3"] = &valState{ 1355 data: ValidatorData{ 1356 ID: "node3", 1357 TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=", 1358 }, 1359 blockAdded: 2, 1360 status: ValidatorStatusTendermint, 1361 heartbeatTracker: &validatorHeartbeatTracker{}, 1362 } 1363 topology.validators["node4"] = &valState{ 1364 data: ValidatorData{ 1365 ID: "node4", 1366 TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=", 1367 }, 1368 blockAdded: 3, 1369 status: ValidatorStatusTendermint, 1370 heartbeatTracker: &validatorHeartbeatTracker{}, 1371 } 1372 topology.validators["node5"] = &valState{ 1373 data: ValidatorData{ 1374 ID: "node5", 1375 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1376 }, 1377 blockAdded: 4, 1378 status: ValidatorStatusPending, 1379 heartbeatTracker: &validatorHeartbeatTracker{}, 1380 } 1381 topology.validators["node6"] = &valState{ 1382 data: ValidatorData{ 1383 ID: "node6", 1384 TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=", 1385 }, 1386 blockAdded: 4, 1387 status: ValidatorStatusPending, 1388 heartbeatTracker: &validatorHeartbeatTracker{}, 1389 } 1390 1391 perfScore := map[string]num.Decimal{ 1392 "node1": decimalOne, 1393 "node2": decimalOne, 1394 "node3": decimalOne, 1395 "node4": decimalOne, 1396 "node5": decimalOne, 1397 "node6": decimalOne, 1398 } 1399 1400 ranking := map[string]num.Decimal{ 1401 "node1": num.DecimalFromFloat(0.0), // this TM validator has zero score so will get demoted 1402 "node2": num.DecimalFromFloat(0.8), // everyone else should be end up being TM due to the slot space, and a promotion 1403 "node3": num.DecimalFromFloat(0.8), 1404 "node4": num.DecimalFromFloat(0.8), 1405 "node5": num.DecimalFromFloat(0.8), 1406 "node6": num.DecimalFromFloat(0.8), 1407 } 1408 1409 delegations := []*types.ValidatorData{ 1410 {NodeID: "node1", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1411 {NodeID: "node2", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1412 {NodeID: "node3", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1413 {NodeID: "node4", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1414 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1415 {NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1416 {NodeID: "node6", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)}, 1417 } 1418 topology.rng = rand.New(rand.NewSource(1000)) 1419 // increase the number of tendermint validators to 5 1420 topology.currentBlockHeight = 1000 1421 topology.numberOfErsatzValidators = 1 1422 topology.numberOfTendermintValidators = 5 1423 topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)}) 1424 1425 // node5 should get promoted due to and increase in slots while node6 gets promoted and swapped with node1 1426 require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node1"].status]) 1427 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status]) 1428 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node3"].status]) 1429 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node4"].status]) 1430 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status]) 1431 require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node6"].status]) 1432 } 1433 1434 type DummyMultiSigTopology struct{} 1435 1436 func (*DummyMultiSigTopology) ChainID() string { 1437 return "12" 1438 } 1439 1440 func (*DummyMultiSigTopology) IsSigner(address string) bool { 1441 return true 1442 } 1443 1444 func (*DummyMultiSigTopology) ExcessSigners(addresses []string) bool { 1445 return false 1446 } 1447 1448 func (*DummyMultiSigTopology) GetThreshold() uint32 { 1449 return 666 1450 } 1451 1452 func (*DummyMultiSigTopology) GetSigners() []string { 1453 return []string{} 1454 }