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