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  }