github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/validator/weighted.go (about) 1 // Modifications Copyright 2019 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/validator/default.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package validator 22 23 import ( 24 "errors" 25 "fmt" 26 "math" 27 "math/big" 28 "math/rand" 29 "reflect" 30 "sort" 31 "strconv" 32 "strings" 33 "sync" 34 "sync/atomic" 35 36 "github.com/klaytn/klaytn/common" 37 "github.com/klaytn/klaytn/consensus" 38 "github.com/klaytn/klaytn/consensus/istanbul" 39 "github.com/klaytn/klaytn/fork" 40 "github.com/klaytn/klaytn/params" 41 "github.com/klaytn/klaytn/reward" 42 ) 43 44 type weightedValidator struct { 45 address common.Address 46 47 rewardAddress atomic.Value 48 votingPower uint64 // TODO-Klaytn-Issue1336 This should be updated for governance implementation 49 weight uint64 50 } 51 52 func (val *weightedValidator) Address() common.Address { 53 return val.address 54 } 55 56 func (val *weightedValidator) String() string { 57 return val.Address().String() 58 } 59 60 func (val *weightedValidator) Equal(val2 *weightedValidator) bool { 61 return val.address == val2.address 62 } 63 64 func (val *weightedValidator) Hash() int64 { 65 return val.address.Hash().Big().Int64() 66 } 67 68 func (val *weightedValidator) RewardAddress() common.Address { 69 rewardAddress := val.rewardAddress.Load() 70 if rewardAddress == nil { 71 return common.Address{} 72 } 73 return rewardAddress.(common.Address) 74 } 75 76 func (val *weightedValidator) SetRewardAddress(rewardAddress common.Address) { 77 val.rewardAddress.Store(rewardAddress) 78 } 79 80 func (val *weightedValidator) VotingPower() uint64 { 81 return val.votingPower 82 } 83 84 func (val *weightedValidator) Weight() uint64 { 85 return atomic.LoadUint64(&val.weight) 86 } 87 88 func newWeightedValidator(addr common.Address, reward common.Address, votingpower uint64, weight uint64) istanbul.Validator { 89 weightedValidator := &weightedValidator{ 90 address: addr, 91 votingPower: votingpower, 92 weight: weight, 93 } 94 weightedValidator.SetRewardAddress(reward) 95 return weightedValidator 96 } 97 98 type weightedCouncil struct { 99 subSize uint64 100 demotedValidators istanbul.Validators // validators staking KLAYs less than minimum, and not in committee/proposers 101 validators istanbul.Validators // validators staking KLAYs more than and equals to minimum, and in committee/proposers 102 policy istanbul.ProposerPolicy 103 104 proposer atomic.Value // istanbul.Validator 105 validatorMu sync.RWMutex // this validator mutex protects concurrent usage of validators and demotedValidators 106 selector istanbul.ProposalSelector 107 108 // TODO-Klaytn-Governance proposers means that the proposers for next block, so refactor it. 109 // proposers are determined on a specific block, but it can be removed after votes. 110 proposers []istanbul.Validator 111 proposersBlockNum uint64 // block number when proposers is determined 112 113 stakingInfo *reward.StakingInfo 114 115 blockNum uint64 // block number when council is determined 116 } 117 118 func RecoverWeightedCouncilProposer(valSet istanbul.ValidatorSet, proposerAddrs []common.Address) { 119 weightedCouncil, ok := valSet.(*weightedCouncil) 120 if !ok { 121 logger.Error("Not weightedCouncil type. Return without recovering.") 122 return 123 } 124 125 proposers := []istanbul.Validator{} 126 127 for i, proposerAddr := range proposerAddrs { 128 _, val := weightedCouncil.GetByAddress(proposerAddr) 129 if val == nil { 130 logger.Error("Proposer is not available now.", "proposer address", proposerAddr) 131 } 132 proposers = append(proposers, val) 133 134 // TODO-Klaytn-Issue1166 Disable Trace log later 135 logger.Trace("RecoverWeightedCouncilProposer() proposers", "i", i, "address", val.Address().String()) 136 } 137 weightedCouncil.proposers = proposers 138 } 139 140 func NewWeightedCouncil(addrs []common.Address, demotedAddrs []common.Address, rewards []common.Address, votingPowers []uint64, weights []uint64, policy istanbul.ProposerPolicy, committeeSize uint64, blockNum uint64, proposersBlockNum uint64, chain consensus.ChainReader) *weightedCouncil { 141 if policy != istanbul.WeightedRandom { 142 logger.Error("unsupported proposer policy for weighted council", "policy", policy) 143 return nil 144 } 145 146 valSet := &weightedCouncil{} 147 valSet.policy = policy 148 149 // prepare rewards if necessary 150 if rewards == nil { 151 rewards = make([]common.Address, len(addrs)) 152 for i := range addrs { 153 rewards[i] = common.Address{} 154 } 155 } 156 157 // prepare weights if necessary 158 if weights == nil { 159 // initialize with 0 weight. 160 weights = make([]uint64, len(addrs)) 161 } 162 163 // prepare votingPowers if necessary 164 if votingPowers == nil { 165 votingPowers = make([]uint64, len(addrs)) 166 if chain == nil { 167 logger.Crit("Requires chain to initialize voting powers.") 168 } 169 170 //stateDB, err := chain.State() 171 //if err != nil { 172 // logger.Crit("Failed to get statedb from chain.") 173 //} 174 175 for i := range addrs { 176 // TODO-Klaytn-TokenEconomy: Use default value until the formula to calculate votingpower released 177 votingPowers[i] = 1000 178 //staking := stateDB.GetBalance(addr) 179 //if staking.Cmp(common.Big0) == 0 { 180 // votingPowers[i] = 1 181 //} else { 182 // votingPowers[i] = 2 183 //} 184 } 185 } 186 187 if len(addrs) != len(rewards) || 188 len(addrs) != len(votingPowers) || 189 len(addrs) != len(weights) { 190 logger.Error("incomplete information for weighted council", "num addrs", len(addrs), "num rewards", len(rewards), "num votingPowers", len(votingPowers), "num weights", len(weights)) 191 return nil 192 } 193 194 // init validators 195 valSet.validators = make([]istanbul.Validator, len(addrs)) 196 for i, addr := range addrs { 197 valSet.validators[i] = newWeightedValidator(addr, rewards[i], votingPowers[i], weights[i]) 198 } 199 200 // sort validators 201 sort.Sort(valSet.validators) 202 203 // init demoted validators 204 valSet.demotedValidators = make([]istanbul.Validator, len(demotedAddrs)) 205 for i, addr := range demotedAddrs { 206 valSet.demotedValidators[i] = newWeightedValidator(addr, common.Address{}, 1000, 0) 207 } 208 209 // sort demoted validators 210 sort.Sort(valSet.demotedValidators) 211 212 // init proposer 213 if valSet.Size() > 0 { 214 valSet.proposer.Store(valSet.GetByIndex(0)) 215 } 216 valSet.SetSubGroupSize(committeeSize) 217 valSet.selector = weightedRandomProposer 218 219 valSet.blockNum = blockNum 220 valSet.proposers = make([]istanbul.Validator, len(addrs)) 221 copy(valSet.proposers, valSet.validators) 222 valSet.proposersBlockNum = proposersBlockNum 223 224 logger.Trace("Allocate new weightedCouncil", "weightedCouncil", valSet) 225 226 return valSet 227 } 228 229 func GetWeightedCouncilData(valSet istanbul.ValidatorSet) (validators []common.Address, demotedValidators []common.Address, rewardAddrs []common.Address, votingPowers []uint64, weights []uint64, proposers []common.Address, proposersBlockNum uint64) { 230 weightedCouncil, ok := valSet.(*weightedCouncil) 231 if !ok { 232 logger.Error("not weightedCouncil type.") 233 return 234 } 235 236 if weightedCouncil.Policy() == istanbul.WeightedRandom { 237 numVals := len(weightedCouncil.validators) 238 validators = make([]common.Address, numVals) 239 rewardAddrs = make([]common.Address, numVals) 240 votingPowers = make([]uint64, numVals) 241 weights = make([]uint64, numVals) 242 for i, val := range weightedCouncil.List() { 243 weightedVal := val.(*weightedValidator) 244 validators[i] = weightedVal.address 245 rewardAddrs[i] = weightedVal.RewardAddress() 246 votingPowers[i] = weightedVal.votingPower 247 weights[i] = atomic.LoadUint64(&weightedVal.weight) 248 } 249 250 numDemoted := len(weightedCouncil.demotedValidators) 251 demotedValidators = make([]common.Address, numDemoted) 252 for i, val := range weightedCouncil.demotedValidators { 253 demotedValidators[i] = val.Address() 254 } 255 256 proposers = make([]common.Address, len(weightedCouncil.proposers)) 257 for i, proposer := range weightedCouncil.proposers { 258 proposers[i] = proposer.Address() 259 } 260 proposersBlockNum = weightedCouncil.proposersBlockNum 261 } else { 262 logger.Error("invalid proposer policy for weightedCouncil") 263 } 264 return 265 } 266 267 func weightedRandomProposer(valSet istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator { 268 weightedCouncil, ok := valSet.(*weightedCouncil) 269 if !ok { 270 logger.Error("weightedRandomProposer() Not weightedCouncil type.") 271 return nil 272 } 273 274 numProposers := len(weightedCouncil.proposers) 275 if numProposers == 0 { 276 logger.Error("weightedRandomProposer() No available proposers.") 277 return nil 278 } 279 280 // At Refresh(), proposers is already randomly shuffled considering weights. 281 // So let's just round robin this array 282 blockNum := weightedCouncil.blockNum 283 picker := (blockNum + round - params.CalcProposerBlockNumber(blockNum+1)) % uint64(numProposers) 284 proposer := weightedCouncil.proposers[picker] 285 286 // Enable below more detailed log when debugging 287 // logger.Trace("Select a proposer using weighted random", "proposer", proposer.String(), "picker", picker, "blockNum of council", blockNum, "round", round, "blockNum of proposers updated", weightedCouncil.proposersBlockNum, "number of proposers", numProposers, "all proposers", weightedCouncil.proposers) 288 289 return proposer 290 } 291 292 func (valSet *weightedCouncil) Size() uint64 { 293 valSet.validatorMu.RLock() 294 defer valSet.validatorMu.RUnlock() 295 return uint64(len(valSet.validators)) 296 } 297 298 func (valSet *weightedCouncil) SubGroupSize() uint64 { 299 return valSet.subSize 300 } 301 302 // SetSubGroupSize sets committee size of the valSet. 303 func (valSet *weightedCouncil) SetSubGroupSize(size uint64) { 304 if size == 0 { 305 logger.Error("cannot assign committee size to 0") 306 return 307 } 308 valSet.subSize = size 309 } 310 311 func (valSet *weightedCouncil) List() []istanbul.Validator { 312 valSet.validatorMu.RLock() 313 defer valSet.validatorMu.RUnlock() 314 return valSet.validators 315 } 316 317 func (valSet *weightedCouncil) DemotedList() []istanbul.Validator { 318 valSet.validatorMu.RLock() 319 defer valSet.validatorMu.RUnlock() 320 return valSet.demotedValidators 321 } 322 323 // SubList composes a committee after setting a proposer with a default value. 324 // This functions returns whole validators if it failed to compose a committee. 325 func (valSet *weightedCouncil) SubList(prevHash common.Hash, view *istanbul.View) []istanbul.Validator { 326 // TODO-Klaytn-Istanbul: investigate whether `valSet.GetProposer().Address()` is a proper value 327 // TODO-Klaytn-Istanbul: or the proposer should be calculated based on `view` 328 return valSet.SubListWithProposer(prevHash, valSet.GetProposer().Address(), view) 329 } 330 331 // SubListWithProposer composes a committee with given parameters. 332 // The first member of the committee is set to the given proposer without calculating proposer with the given `view`. 333 // The second member of the committee is calculated with a round number of the given view and `valSet.blockNum`. 334 // The reset of the committee is selected with a random seed derived from `prevHash`. 335 // This functions returns whole validators if it failed to compose a committee. 336 func (valSet *weightedCouncil) SubListWithProposer(prevHash common.Hash, proposerAddr common.Address, view *istanbul.View) []istanbul.Validator { 337 valSet.validatorMu.RLock() 338 defer valSet.validatorMu.RUnlock() 339 340 validators := valSet.validators 341 validatorSize := uint64(len(validators)) 342 committeeSize := valSet.subSize 343 344 // return early if the committee size is equal or larger than the validator size 345 if committeeSize >= validatorSize { 346 return validators 347 } 348 349 // find the proposer 350 proposerIdx, proposer := valSet.getByAddress(proposerAddr) 351 if proposerIdx < 0 { 352 logger.Error("invalid index of the proposer", 353 "addr", proposerAddr.String(), "index", proposerIdx) 354 return validators 355 } 356 357 // return early if the committee size is 1 358 if committeeSize == 1 { 359 return []istanbul.Validator{proposer} 360 } 361 362 // find the next proposer 363 var nextProposer istanbul.Validator 364 idx := uint64(1) 365 for { 366 // ensure finishing this loop 367 if idx > params.ProposerUpdateInterval() { 368 logger.Error("failed to find the next proposer", "validatorSize", validatorSize, 369 "proposer", proposer.Address().String(), "validatorAddrs", validators.AddressStringList()) 370 return validators 371 } 372 nextProposer = valSet.selector(valSet, proposerAddr, view.Round.Uint64()+idx) 373 if proposer.Address() != nextProposer.Address() { 374 break 375 } 376 idx++ 377 } 378 nextProposerIdx, _ := valSet.getByAddress(nextProposer.Address()) 379 if nextProposerIdx < 0 { 380 logger.Error("invalid index of the next proposer", 381 "addr", nextProposer.Address().String(), "index", nextProposerIdx) 382 return validators 383 } 384 385 // seed will be used to select a random committee 386 seed, err := ConvertHashToSeed(prevHash) 387 if fork.Rules(view.Sequence).IsIstanbul { 388 seed += view.Round.Int64() 389 } 390 if err != nil { 391 logger.Error("failed to convert hash to seed", "prevHash", prevHash, "err", err) 392 return validators 393 } 394 395 // select a random committee 396 committee := SelectRandomCommittee(validators, committeeSize, seed, proposerIdx, nextProposerIdx) 397 if committee == nil { 398 committee = validators 399 } 400 401 logger.Trace("composed committee", "valSet.Number", valSet.blockNum, "prevHash", prevHash.Hex(), 402 "proposerAddr", proposerAddr, "committee", committee, "committee size", len(committee), "valSet.subSize", committeeSize) 403 404 return committee 405 } 406 407 func (valSet *weightedCouncil) CheckInSubList(prevHash common.Hash, view *istanbul.View, addr common.Address) bool { 408 for _, val := range valSet.SubList(prevHash, view) { 409 if val.Address() == addr { 410 return true 411 } 412 } 413 return false 414 } 415 416 func (valSet *weightedCouncil) IsSubSet() bool { 417 // TODO-Klaytn-RemoveLater We don't use this interface anymore. Eventually let's remove this function from ValidatorSet interface. 418 return valSet.Size() > valSet.subSize 419 } 420 421 func (valSet *weightedCouncil) GetByIndex(i uint64) istanbul.Validator { 422 valSet.validatorMu.RLock() 423 defer valSet.validatorMu.RUnlock() 424 if i < uint64(len(valSet.validators)) { 425 return valSet.validators[i] 426 } 427 return nil 428 } 429 430 func (valSet *weightedCouncil) getByAddress(addr common.Address) (int, istanbul.Validator) { 431 for i, val := range valSet.validators { 432 if addr == val.Address() { 433 return i, val 434 } 435 } 436 // TODO-Klaytn-Istanbul: Enable this log when non-committee nodes don't call `core.startNewRound()` 437 /*logger.Warn("failed to find an address in the validator list", 438 "address", addr, "validatorAddrs", valSet.validators.AddressStringList())*/ 439 return -1, nil 440 } 441 442 func (valSet *weightedCouncil) getDemotedByAddress(addr common.Address) (int, istanbul.Validator) { 443 for i, val := range valSet.demotedValidators { 444 if addr == val.Address() { 445 return i, val 446 } 447 } 448 return -1, nil 449 } 450 451 func (valSet *weightedCouncil) GetByAddress(addr common.Address) (int, istanbul.Validator) { 452 valSet.validatorMu.RLock() 453 defer valSet.validatorMu.RUnlock() 454 return valSet.getByAddress(addr) 455 } 456 457 func (valSet *weightedCouncil) GetDemotedByAddress(addr common.Address) (int, istanbul.Validator) { 458 valSet.validatorMu.RLock() 459 defer valSet.validatorMu.RUnlock() 460 return valSet.getDemotedByAddress(addr) 461 } 462 463 func (valSet *weightedCouncil) GetProposer() istanbul.Validator { 464 // TODO-Klaytn-Istanbul: nil check for valSet.proposer is needed 465 // logger.Trace("GetProposer()", "proposer", valSet.proposer) 466 return valSet.proposer.Load().(istanbul.Validator) 467 } 468 469 func (valSet *weightedCouncil) IsProposer(address common.Address) bool { 470 _, val := valSet.GetByAddress(address) 471 return reflect.DeepEqual(valSet.GetProposer(), val) 472 } 473 474 func (valSet *weightedCouncil) chooseProposerByRoundRobin(lastProposer common.Address, round uint64) istanbul.Validator { 475 seed := uint64(0) 476 if emptyAddress(lastProposer) { 477 seed = round 478 } else { 479 offset := 0 480 if idx, val := valSet.getByAddress(lastProposer); val != nil { 481 offset = idx 482 } 483 seed = uint64(offset) + round 484 } 485 pick := seed % uint64(len(valSet.validators)) 486 return valSet.validators[pick] 487 } 488 489 func (valSet *weightedCouncil) CalcProposer(lastProposer common.Address, round uint64) { 490 valSet.validatorMu.RLock() 491 defer valSet.validatorMu.RUnlock() 492 493 newProposer := valSet.selector(valSet, lastProposer, round) 494 if newProposer == nil { 495 if len(valSet.validators) == 0 { 496 // TODO-Klaytn We must make a policy about the mininum number of validators, which can prevent this case. 497 logger.Error("NO VALIDATOR! Use lastProposer as a workaround") 498 newProposer = newWeightedValidator(lastProposer, common.Address{}, 0, 0) 499 } else { 500 logger.Warn("Failed to select a new proposer, thus fall back to roundRobinProposer") 501 newProposer = valSet.chooseProposerByRoundRobin(lastProposer, round) 502 } 503 } 504 505 logger.Debug("Update a proposer", "old", valSet.proposer, "new", newProposer, "last proposer", lastProposer.String(), "round", round, "blockNum of council", valSet.blockNum, "blockNum of proposers", valSet.proposersBlockNum) 506 valSet.proposer.Store(newProposer) 507 } 508 509 func (valSet *weightedCouncil) AddValidator(address common.Address) bool { 510 valSet.validatorMu.Lock() 511 defer valSet.validatorMu.Unlock() 512 for _, v := range valSet.validators { 513 if v.Address() == address { 514 return false 515 } 516 } 517 for _, v := range valSet.demotedValidators { 518 if v.Address() == address { 519 return false 520 } 521 } 522 523 // TODO-Klaytn-Governance the new validator is added on validators only and demoted after `Refresh` method. It is better to update here if it is demoted ones. 524 // TODO-Klaytn-Issue1336 Update for governance implementation. How to determine initial value for rewardAddress and votingPower ? 525 valSet.validators = append(valSet.validators, newWeightedValidator(address, common.Address{}, 1000, 0)) 526 527 // sort validator 528 sort.Sort(valSet.validators) 529 return true 530 } 531 532 // removeValidatorFromProposers makes new candidate proposers by removing a validator with given address from existing proposers. 533 func (valSet *weightedCouncil) removeValidatorFromProposers(address common.Address) { 534 newProposers := make([]istanbul.Validator, 0, len(valSet.proposers)) 535 536 for _, v := range valSet.proposers { 537 if v.Address() != address { 538 newProposers = append(newProposers, v) 539 } 540 } 541 542 valSet.proposers = newProposers 543 } 544 545 func (valSet *weightedCouncil) RemoveValidator(address common.Address) bool { 546 valSet.validatorMu.Lock() 547 defer valSet.validatorMu.Unlock() 548 549 for i, v := range valSet.validators { 550 if v.Address() == address { 551 valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...) 552 valSet.removeValidatorFromProposers(address) 553 return true 554 } 555 } 556 for i, v := range valSet.demotedValidators { 557 if v.Address() == address { 558 valSet.demotedValidators = append(valSet.demotedValidators[:i], valSet.demotedValidators[i+1:]...) 559 return true 560 } 561 } 562 return false 563 } 564 565 func (valSet *weightedCouncil) ReplaceValidators(vals []istanbul.Validator) bool { 566 valSet.validatorMu.Lock() 567 defer valSet.validatorMu.Unlock() 568 569 valSet.validators = istanbul.Validators(make([]istanbul.Validator, len(vals))) 570 copy(valSet.validators, istanbul.Validators(vals)) 571 return true 572 } 573 574 func (valSet *weightedCouncil) GetValidators() []istanbul.Validator { 575 return valSet.validators 576 } 577 578 func (valSet *weightedCouncil) Copy() istanbul.ValidatorSet { 579 valSet.validatorMu.RLock() 580 defer valSet.validatorMu.RUnlock() 581 582 newWeightedCouncil := weightedCouncil{ 583 subSize: valSet.subSize, 584 policy: valSet.policy, 585 proposer: valSet.proposer, 586 selector: valSet.selector, 587 stakingInfo: valSet.stakingInfo, 588 proposersBlockNum: valSet.proposersBlockNum, 589 blockNum: valSet.blockNum, 590 } 591 newWeightedCouncil.validators = make([]istanbul.Validator, len(valSet.validators)) 592 copy(newWeightedCouncil.validators, valSet.validators) 593 594 newWeightedCouncil.demotedValidators = make([]istanbul.Validator, len(valSet.demotedValidators)) 595 copy(newWeightedCouncil.demotedValidators, valSet.demotedValidators) 596 597 newWeightedCouncil.proposers = make([]istanbul.Validator, len(valSet.proposers)) 598 copy(newWeightedCouncil.proposers, valSet.proposers) 599 600 return &newWeightedCouncil 601 } 602 603 func (valSet *weightedCouncil) F() int { 604 if valSet.Size() > valSet.subSize { 605 return int(math.Ceil(float64(valSet.subSize)/3)) - 1 606 } else { 607 return int(math.Ceil(float64(valSet.Size())/3)) - 1 608 } 609 } 610 611 func (valSet *weightedCouncil) Policy() istanbul.ProposerPolicy { return valSet.policy } 612 613 // Refresh recalculates up-to-date proposers only when blockNum is the proposer update interval. 614 // It returns an error if it can't make up-to-date proposers 615 // (1) due toe wrong parameters 616 // (2) due to lack of staking information 617 // It returns no error when weightedCouncil: 618 // (1) already has up-do-date proposers 619 // (2) successfully calculated up-do-date proposers 620 func (valSet *weightedCouncil) Refresh(hash common.Hash, blockNum uint64, config *params.ChainConfig, isSingle bool, governingNode common.Address, minStaking uint64) error { 621 // TODO-Klaytn-Governance divide the following logic into two parts: proposers update / validators update 622 valSet.validatorMu.Lock() 623 defer valSet.validatorMu.Unlock() 624 625 // Check errors 626 numValidators := len(valSet.validators) 627 if numValidators == 0 { 628 return errors.New("No validator") 629 } 630 631 hashString := strings.TrimPrefix(hash.Hex(), "0x") 632 if len(hashString) > 15 { 633 hashString = hashString[:15] 634 } 635 seed, err := strconv.ParseInt(hashString, 16, 64) 636 if err != nil { 637 return err 638 } 639 640 newStakingInfo := reward.GetStakingInfo(blockNum + 1) 641 if newStakingInfo == nil { 642 // Just return without updating proposer 643 return errors.New("skip refreshing proposers due to no staking info") 644 } 645 valSet.stakingInfo = newStakingInfo 646 647 blockNumBig := new(big.Int).SetUint64(blockNum) 648 chainRules := config.Rules(blockNumBig) 649 650 candidates := append(valSet.validators, valSet.demotedValidators...) 651 weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, newStakingInfo) 652 if err != nil { 653 return err 654 } 655 656 if chainRules.IsIstanbul { 657 var demotedValidators []*weightedValidator 658 659 weightedValidators, stakingAmounts, demotedValidators, _ = filterValidators(isSingle, governingNode, weightedValidators, stakingAmounts, minStaking) 660 valSet.setValidators(weightedValidators, demotedValidators) 661 } 662 663 if valSet.proposersBlockNum == blockNum { 664 // proposers are already refreshed 665 return nil 666 } 667 668 // weight and gini were neutralized after Kore hard fork 669 if chainRules.IsKore { 670 setZeroWeight(weightedValidators) 671 } else { 672 totalStaking, _ := calcTotalAmount(weightedValidators, newStakingInfo, stakingAmounts) 673 calcWeight(weightedValidators, stakingAmounts, totalStaking) 674 } 675 676 valSet.refreshProposers(seed, blockNum) 677 678 logger.Debug("Refresh done.", "blockNum", blockNum, "hash", hash, "valSet.blockNum", valSet.blockNum, "stakingInfo.BlockNum", valSet.stakingInfo.BlockNum) 679 logger.Debug("New proposers calculated", "new proposers", valSet.proposers) 680 681 return nil 682 } 683 684 // setValidators converts weighted validator slice to istanbul.Validators and sets them to the council. 685 func (valSet *weightedCouncil) setValidators(validators []*weightedValidator, demoted []*weightedValidator) { 686 var ( 687 newValidators istanbul.Validators 688 newDemoted istanbul.Validators 689 ) 690 691 for _, val := range validators { 692 newValidators = append(newValidators, val) 693 } 694 695 for _, val := range demoted { 696 newDemoted = append(newDemoted, val) 697 } 698 699 sort.Sort(newValidators) 700 sort.Sort(newDemoted) 701 702 valSet.validators = newValidators 703 valSet.demotedValidators = newDemoted 704 } 705 706 // filterValidators divided the given weightedValidators into two group filtered by the minimum amount of staking. 707 // If governance mode is single, the governing node will always be a validator. 708 // If no validator has enough KLAYs, all become validators. 709 func filterValidators(isSingleMode bool, govNodeAddr common.Address, weightedValidators []*weightedValidator, stakingAmounts []float64, minStaking uint64) ([]*weightedValidator, []float64, []*weightedValidator, []float64) { 710 var ( 711 newWeightedValidators []*weightedValidator 712 newWeightedDemoted []*weightedValidator 713 govNode *weightedValidator 714 newValidatorsStaking []float64 715 newDemotedStaking []float64 716 govNodeStaking float64 717 ) 718 for idx, val := range stakingAmounts { 719 if isSingleMode && govNodeAddr == weightedValidators[idx].Address() { 720 govNode = weightedValidators[idx] 721 govNodeStaking = val 722 } else if uint64(val) >= minStaking { 723 newWeightedValidators = append(newWeightedValidators, weightedValidators[idx]) 724 newValidatorsStaking = append(newValidatorsStaking, val) 725 } else { 726 newWeightedDemoted = append(newWeightedDemoted, weightedValidators[idx]) 727 newDemotedStaking = append(newDemotedStaking, val) 728 } 729 } 730 731 // when no validator has more than minimum staking amount of KLAYs, all members are in validators 732 if len(newWeightedValidators) <= 0 { 733 // 1. if governance mode is not single, 734 // 2. if governance mode is single and governing node does not have minimum staking amount of KLAYs as well 735 if !isSingleMode || uint64(govNodeStaking) < minStaking { 736 newWeightedValidators, newValidatorsStaking = newWeightedDemoted, newDemotedStaking 737 newWeightedDemoted, newDemotedStaking = []*weightedValidator{}, []float64{} 738 logger.Debug("there is no council member staking more than the minimum, so all become validators", "numValidators", len(newWeightedValidators), "isSingleMode", isSingleMode, "govNodeAddr", govNodeAddr, "govNodeStaking", govNodeStaking, "minStaking", minStaking) 739 } 740 } 741 742 // if the governance mode is single, governing node is added to validators all the time. 743 if isSingleMode { 744 newWeightedValidators = append(newWeightedValidators, govNode) 745 newValidatorsStaking = append(newValidatorsStaking, govNodeStaking) 746 } 747 return newWeightedValidators, newValidatorsStaking, newWeightedDemoted, newDemotedStaking 748 } 749 750 // getStakingAmountsOfValidators calculates stakingAmounts of validators. 751 // If validators have multiple staking contracts, stakingAmounts will be a sum of stakingAmounts with the same rewardAddress. 752 // - []*weightedValidator : a list of validators which type is converted to weightedValidator 753 // - []float64 : a list of stakingAmounts. 754 func getStakingAmountsOfValidators(validators istanbul.Validators, stakingInfo *reward.StakingInfo) ([]*weightedValidator, []float64, error) { 755 numValidators := len(validators) 756 weightedValidators := make([]*weightedValidator, numValidators) 757 stakingAmounts := make([]float64, numValidators) 758 addedStaking := make([]bool, len(stakingInfo.CouncilNodeAddrs)) 759 760 for vIdx, val := range validators { 761 weightedVal, ok := val.(*weightedValidator) 762 if !ok { 763 return nil, nil, errors.New(fmt.Sprintf("not weightedValidator. val=%s", val.Address().String())) 764 } 765 weightedValidators[vIdx] = weightedVal 766 767 sIdx, err := stakingInfo.GetIndexByNodeAddress(weightedVal.address) 768 if err == nil { 769 rewardAddr := stakingInfo.CouncilRewardAddrs[sIdx] 770 weightedVal.SetRewardAddress(rewardAddr) 771 stakingAmounts[vIdx] = float64(stakingInfo.CouncilStakingAmounts[sIdx]) 772 addedStaking[sIdx] = true 773 } else { 774 weightedVal.SetRewardAddress(common.Address{}) 775 } 776 } 777 778 for sIdx, isAdded := range addedStaking { 779 if isAdded { 780 continue 781 } 782 for vIdx, val := range weightedValidators { 783 if val.RewardAddress() == stakingInfo.CouncilRewardAddrs[sIdx] { 784 stakingAmounts[vIdx] += float64(stakingInfo.CouncilStakingAmounts[sIdx]) 785 break 786 } 787 } 788 } 789 790 logger.Debug("stakingAmounts of validators", "validators", weightedValidators, "stakingAmounts", stakingAmounts) 791 return weightedValidators, stakingAmounts, nil 792 } 793 794 // calcTotalAmount calculates totalAmount of stakingAmounts. 795 // If UseGini is true, gini is reflected to stakingAmounts. 796 func calcTotalAmount(weightedValidators []*weightedValidator, stakingInfo *reward.StakingInfo, stakingAmounts []float64) (float64, float64) { 797 totalStaking := float64(0) 798 // stakingInfo.Gini is calculated among all CNs (i.e. AddressBook.cnStakingContracts) 799 // But we want the gini calculated among the subset of CNs (i.e. validators) 800 gini := reward.DefaultGiniCoefficient 801 802 if len(stakingInfo.CouncilNodeAddrs) == 0 { 803 return totalStaking, gini 804 } 805 806 if stakingInfo.UseGini { 807 var tempStakingAmounts []float64 808 for vIdx, val := range weightedValidators { 809 _, err := stakingInfo.GetIndexByNodeAddress(val.address) 810 if err == nil { 811 tempStakingAmounts = append(tempStakingAmounts, stakingAmounts[vIdx]) 812 } 813 } 814 gini = reward.CalcGiniCoefficient(tempStakingAmounts) 815 816 for i := range stakingAmounts { 817 stakingAmounts[i] = math.Round(math.Pow(stakingAmounts[i], 1.0/(1+gini))) 818 totalStaking += stakingAmounts[i] 819 } 820 } else { 821 for _, stakingAmount := range stakingAmounts { 822 totalStaking += stakingAmount 823 } 824 } 825 826 logger.Debug("calculate totalStaking", "UseGini", stakingInfo.UseGini, "Gini", gini, "totalStaking", totalStaking, "stakingAmounts", stakingAmounts) 827 return totalStaking, gini 828 } 829 830 // setZeroWeight makes each validator's weight to zero 831 func setZeroWeight(weightedValidators []*weightedValidator) { 832 for _, weightedVal := range weightedValidators { 833 atomic.StoreUint64(&weightedVal.weight, 0) 834 } 835 } 836 837 // calcWeight updates each validator's weight based on the ratio of its staking amount vs. the total staking amount. 838 func calcWeight(weightedValidators []*weightedValidator, stakingAmounts []float64, totalStaking float64) { 839 localLogger := logger.NewWith() 840 if totalStaking > 0 { 841 for i, weightedVal := range weightedValidators { 842 weight := uint64(math.Round(stakingAmounts[i] * 100 / totalStaking)) 843 if weight <= 0 { 844 // A validator, who holds zero or small stake, has minimum weight, 1. 845 weight = 1 846 } 847 atomic.StoreUint64(&weightedVal.weight, weight) 848 localLogger = localLogger.NewWith(weightedVal.String(), weight) 849 } 850 } else { 851 for _, weightedVal := range weightedValidators { 852 atomic.StoreUint64(&weightedVal.weight, 0) 853 localLogger = localLogger.NewWith(weightedVal.String(), 0) 854 } 855 } 856 localLogger.Debug("calculation weight finished") 857 } 858 859 func (valSet *weightedCouncil) refreshProposers(seed int64, blockNum uint64) { 860 var candidateValsIdx []int // This is a slice which stores index of validator. it is used for shuffling 861 862 for index, val := range valSet.validators { 863 weight := val.Weight() 864 for i := uint64(0); i < weight; i++ { 865 candidateValsIdx = append(candidateValsIdx, index) 866 } 867 } 868 869 if len(candidateValsIdx) == 0 { 870 // All validators has zero weight. Let's use all validators as candidate proposers. 871 for index := 0; index < len(valSet.validators); index++ { 872 candidateValsIdx = append(candidateValsIdx, index) 873 } 874 logger.Trace("Refresh uses all validators as candidate proposers, because all weight is zero.", "candidateValsIdx", candidateValsIdx) 875 } 876 877 proposers := make([]istanbul.Validator, len(candidateValsIdx)) 878 879 limit := len(candidateValsIdx) 880 picker := rand.New(rand.NewSource(seed)) 881 882 // shuffle 883 for i := 0; i < limit; i++ { 884 randIndex := picker.Intn(limit) 885 candidateValsIdx[i], candidateValsIdx[randIndex] = candidateValsIdx[randIndex], candidateValsIdx[i] 886 } 887 888 for i := 0; i < limit; i++ { 889 proposers[i] = valSet.validators[candidateValsIdx[i]] 890 // Below log is too verbose. Use is only when debugging. 891 // logger.Trace("Refresh calculates new proposers", "i", i, "proposers[i]", proposers[i].String()) 892 } 893 894 valSet.proposers = proposers 895 valSet.proposersBlockNum = blockNum 896 } 897 898 func (valSet *weightedCouncil) SetBlockNum(blockNum uint64) { 899 valSet.blockNum = blockNum 900 } 901 902 func (valSet *weightedCouncil) Proposers() []istanbul.Validator { 903 return valSet.proposers 904 } 905 906 func (valSet *weightedCouncil) TotalVotingPower() uint64 { 907 sum := uint64(0) 908 for _, v := range valSet.List() { 909 sum += v.VotingPower() 910 } 911 return sum 912 } 913 914 func (valSet *weightedCouncil) Selector(valS istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator { 915 return valSet.selector(valS, lastProposer, round) 916 }