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  }