github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/validator/default.go (about)

     1  // Modifications Copyright 2018 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  	"math"
    25  	"reflect"
    26  	"sort"
    27  	"sync"
    28  	"sync/atomic"
    29  
    30  	"github.com/klaytn/klaytn/common"
    31  	"github.com/klaytn/klaytn/consensus/istanbul"
    32  	"github.com/klaytn/klaytn/fork"
    33  	"github.com/klaytn/klaytn/params"
    34  )
    35  
    36  const (
    37  	defaultSubSetLength = 21
    38  )
    39  
    40  type defaultValidator struct {
    41  	address common.Address
    42  }
    43  
    44  func (val *defaultValidator) Address() common.Address {
    45  	return val.address
    46  }
    47  
    48  func (val *defaultValidator) String() string {
    49  	return val.Address().String()
    50  }
    51  
    52  func (val *defaultValidator) Equal(val2 *defaultValidator) bool {
    53  	return val.address == val2.address
    54  }
    55  
    56  func (val *defaultValidator) Hash() int64 {
    57  	return val.address.Hash().Big().Int64()
    58  }
    59  
    60  func (val *defaultValidator) RewardAddress() common.Address { return common.Address{} }
    61  func (val *defaultValidator) VotingPower() uint64           { return 1000 }
    62  func (val *defaultValidator) Weight() uint64                { return 0 }
    63  
    64  type defaultSet struct {
    65  	subSize uint64
    66  
    67  	validators istanbul.Validators
    68  	policy     istanbul.ProposerPolicy
    69  
    70  	proposer    atomic.Value
    71  	validatorMu sync.RWMutex
    72  	selector    istanbul.ProposalSelector
    73  }
    74  
    75  func newDefaultSet(addrs []common.Address, policy istanbul.ProposerPolicy) *defaultSet {
    76  	valSet := &defaultSet{}
    77  
    78  	valSet.subSize = defaultSubSetLength
    79  	valSet.policy = policy
    80  	// init validators
    81  	valSet.validators = make([]istanbul.Validator, len(addrs))
    82  	for i, addr := range addrs {
    83  		valSet.validators[i] = New(addr)
    84  	}
    85  	// sort validator
    86  	sort.Sort(valSet.validators)
    87  	// init proposer
    88  	if valSet.Size() > 0 {
    89  		valSet.proposer.Store(valSet.GetByIndex(0))
    90  	}
    91  	valSet.selector = roundRobinProposer
    92  	if policy == istanbul.Sticky {
    93  		valSet.selector = stickyProposer
    94  	}
    95  
    96  	return valSet
    97  }
    98  
    99  func newDefaultSubSet(addrs []common.Address, policy istanbul.ProposerPolicy, subSize uint64) *defaultSet {
   100  	valSet := &defaultSet{}
   101  
   102  	valSet.subSize = subSize
   103  	valSet.policy = policy
   104  	// init validators
   105  	valSet.validators = make([]istanbul.Validator, len(addrs))
   106  	for i, addr := range addrs {
   107  		valSet.validators[i] = New(addr)
   108  	}
   109  	// sort validator
   110  	sort.Sort(valSet.validators)
   111  	// init proposer
   112  	if valSet.Size() > 0 {
   113  		valSet.proposer.Store(valSet.GetByIndex(0))
   114  	}
   115  	valSet.selector = roundRobinProposer
   116  	if policy == istanbul.Sticky {
   117  		valSet.selector = stickyProposer
   118  	}
   119  
   120  	return valSet
   121  }
   122  
   123  func (valSet *defaultSet) Size() uint64 {
   124  	valSet.validatorMu.RLock()
   125  	defer valSet.validatorMu.RUnlock()
   126  	return uint64(len(valSet.validators))
   127  }
   128  
   129  func (valSet *defaultSet) SubGroupSize() uint64 {
   130  	return valSet.subSize
   131  }
   132  
   133  // SetSubGroupSize sets committee size of the valSet.
   134  func (valSet *defaultSet) SetSubGroupSize(size uint64) {
   135  	if size == 0 {
   136  		logger.Error("cannot assign committee size to 0")
   137  		return
   138  	}
   139  	valSet.subSize = size
   140  }
   141  
   142  func (valSet *defaultSet) List() []istanbul.Validator {
   143  	valSet.validatorMu.RLock()
   144  	defer valSet.validatorMu.RUnlock()
   145  	return valSet.validators
   146  }
   147  
   148  func (valSet *defaultSet) DemotedList() []istanbul.Validator {
   149  	return nil
   150  }
   151  
   152  // SubList composes a committee after setting a proposer with a default value.
   153  // This functions returns whole validators if it failed to compose a committee.
   154  func (valSet *defaultSet) SubList(prevHash common.Hash, view *istanbul.View) []istanbul.Validator {
   155  	// TODO-Klaytn-Istanbul: investigate whether `valSet.GetProposer().Address()` is a proper value or the proposer should be calculated based on `view`
   156  	proposer := valSet.GetProposer()
   157  	if proposer == nil {
   158  		return valSet.List()
   159  	}
   160  	return valSet.SubListWithProposer(prevHash, proposer.Address(), view)
   161  }
   162  
   163  // SubListWithProposer composes a committee with given parameters.
   164  // The first member of the committee is set to the given proposer without calculating proposer with the given `view`.
   165  // The second member of the committee is calculated with a round number of the given view and `valSet.blockNum`.
   166  // The reset of the committee is selected with a random seed derived from `prevHash`.
   167  // This functions returns whole validators if it failed to compose a committee.
   168  func (valSet *defaultSet) SubListWithProposer(prevHash common.Hash, proposerAddr common.Address, view *istanbul.View) []istanbul.Validator {
   169  	valSet.validatorMu.RLock()
   170  	defer valSet.validatorMu.RUnlock()
   171  
   172  	validators := valSet.validators
   173  	validatorSize := uint64(len(validators))
   174  	committeeSize := valSet.subSize
   175  
   176  	// return early if the committee size is equal or larger than the validator size
   177  	if committeeSize >= validatorSize {
   178  		return validators
   179  	}
   180  
   181  	// find the proposer
   182  	proposerIdx, proposer := valSet.GetByAddress(proposerAddr)
   183  	if proposerIdx < 0 {
   184  		logger.Error("invalid index of the proposer",
   185  			"addr", proposerAddr.String(), "index", proposerIdx)
   186  		return validators
   187  	}
   188  
   189  	// return early if the committee size is 1
   190  	if committeeSize == 1 {
   191  		return []istanbul.Validator{proposer}
   192  	}
   193  
   194  	// find the next proposer
   195  	nextProposer := valSet.selector(valSet, proposer.Address(), view.Round.Uint64())
   196  	nextProposerIdx, _ := valSet.GetByAddress(nextProposer.Address())
   197  	if nextProposerIdx < 0 {
   198  		logger.Error("invalid index of the next proposer",
   199  			"addr", nextProposer.Address().String(), "index", nextProposerIdx)
   200  		return validators
   201  	}
   202  
   203  	// seed will be used to select a random committee
   204  	seed, err := ConvertHashToSeed(prevHash)
   205  	if fork.Rules(view.Sequence).IsIstanbul {
   206  		seed += view.Round.Int64()
   207  	}
   208  	if err != nil {
   209  		logger.Error("failed to convert hash to seed", "prevHash", prevHash, "err", err)
   210  		return validators
   211  	}
   212  
   213  	// select a random committee
   214  	committee := SelectRandomCommittee(validators, committeeSize, seed, proposerIdx, nextProposerIdx)
   215  	if committee == nil {
   216  		committee = validators
   217  	}
   218  
   219  	logger.Trace("composed committee", "prevHash", prevHash.Hex(), "proposerAddr", proposerAddr,
   220  		"committee", committee, "committee size", len(committee), "valSet.subSize", committeeSize)
   221  
   222  	return committee
   223  }
   224  
   225  func (valSet *defaultSet) CheckInSubList(prevHash common.Hash, view *istanbul.View, addr common.Address) bool {
   226  	for _, val := range valSet.SubList(prevHash, view) {
   227  		if val.Address() == addr {
   228  			return true
   229  		}
   230  	}
   231  	return false
   232  }
   233  
   234  func (valSet *defaultSet) IsSubSet() bool {
   235  	return valSet.Size() > valSet.subSize
   236  }
   237  
   238  func (valSet *defaultSet) GetByIndex(i uint64) istanbul.Validator {
   239  	valSet.validatorMu.RLock()
   240  	defer valSet.validatorMu.RUnlock()
   241  	if i < uint64(len(valSet.validators)) {
   242  		return valSet.validators[i]
   243  	}
   244  	return nil
   245  }
   246  
   247  func (valSet *defaultSet) GetByAddress(addr common.Address) (int, istanbul.Validator) {
   248  	for i, val := range valSet.List() {
   249  		if addr == val.Address() {
   250  			return i, val
   251  		}
   252  	}
   253  	// TODO-Klaytn-Istanbul: Enable this log when non-committee nodes don't call `core.startNewRound()`
   254  	// logger.Warn("failed to find an address in the validator list",
   255  	// 	"address", addr, "validatorAddrs", valSet.validators.AddressStringList())
   256  	return -1, nil
   257  }
   258  
   259  func (valSet *defaultSet) GetDemotedByAddress(addr common.Address) (int, istanbul.Validator) {
   260  	return -1, nil
   261  }
   262  
   263  func (valSet *defaultSet) GetProposer() istanbul.Validator {
   264  	proposer := valSet.proposer.Load()
   265  	if proposer == nil {
   266  		logger.Error("Proposer is nil", "validators", valSet.validators)
   267  		return nil
   268  	}
   269  	return proposer.(istanbul.Validator)
   270  }
   271  
   272  func (valSet *defaultSet) IsProposer(address common.Address) bool {
   273  	_, val := valSet.GetByAddress(address)
   274  	return reflect.DeepEqual(valSet.GetProposer(), val)
   275  }
   276  
   277  func (valSet *defaultSet) CalcProposer(lastProposer common.Address, round uint64) {
   278  	valSet.validatorMu.RLock()
   279  	defer valSet.validatorMu.RUnlock()
   280  
   281  	if len(valSet.validators) == 0 {
   282  		logger.Error("List of validators is empty", "validators", len(valSet.validators))
   283  		return
   284  	}
   285  
   286  	valSet.proposer.Store(valSet.selector(valSet, lastProposer, round))
   287  }
   288  
   289  func calcSeed(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) uint64 {
   290  	offset := 0
   291  	if idx, val := valSet.GetByAddress(proposer); val != nil {
   292  		offset = idx
   293  	}
   294  	return uint64(offset) + round
   295  }
   296  
   297  func emptyAddress(addr common.Address) bool {
   298  	return addr == common.Address{}
   299  }
   300  
   301  func roundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator {
   302  	seed := uint64(0)
   303  	if emptyAddress(proposer) {
   304  		seed = round
   305  	} else {
   306  		seed = calcSeed(valSet, proposer, round) + 1
   307  	}
   308  	pick := seed % uint64(valSet.Size())
   309  	return valSet.GetByIndex(pick)
   310  }
   311  
   312  func stickyProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator {
   313  	seed := uint64(0)
   314  	if emptyAddress(proposer) {
   315  		seed = round
   316  	} else {
   317  		seed = calcSeed(valSet, proposer, round)
   318  	}
   319  	pick := seed % uint64(valSet.Size())
   320  	return valSet.GetByIndex(pick)
   321  }
   322  
   323  func (valSet *defaultSet) AddValidator(address common.Address) bool {
   324  	valSet.validatorMu.Lock()
   325  	defer valSet.validatorMu.Unlock()
   326  	for _, v := range valSet.validators {
   327  		if v.Address() == address {
   328  			return false
   329  		}
   330  	}
   331  	valSet.validators = append(valSet.validators, New(address))
   332  	// TODO: we may not need to re-sort it again
   333  	// sort validator
   334  	sort.Sort(valSet.validators)
   335  	return true
   336  }
   337  
   338  func (valSet *defaultSet) RemoveValidator(address common.Address) bool {
   339  	valSet.validatorMu.Lock()
   340  	defer valSet.validatorMu.Unlock()
   341  
   342  	for i, v := range valSet.validators {
   343  		if v.Address() == address {
   344  			valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...)
   345  			return true
   346  		}
   347  	}
   348  	return false
   349  }
   350  
   351  func (valSet *defaultSet) Copy() istanbul.ValidatorSet {
   352  	valSet.validatorMu.RLock()
   353  	defer valSet.validatorMu.RUnlock()
   354  
   355  	addresses := make([]common.Address, 0, len(valSet.validators))
   356  	for _, v := range valSet.validators {
   357  		addresses = append(addresses, v.Address())
   358  	}
   359  
   360  	newValSet := NewSubSet(addresses, valSet.policy, valSet.subSize).(*defaultSet)
   361  	if proposer := valSet.GetProposer(); proposer != nil {
   362  		// Copy the proposer if exist
   363  		if idx, p := newValSet.GetByAddress(proposer.Address()); idx != -1 {
   364  			newValSet.proposer.Store(p)
   365  		}
   366  	}
   367  	return newValSet
   368  }
   369  
   370  func (valSet *defaultSet) F() int {
   371  	if valSet.Size() > valSet.subSize {
   372  		return int(math.Ceil(float64(valSet.subSize)/3)) - 1
   373  	} else {
   374  		return int(math.Ceil(float64(valSet.Size())/3)) - 1
   375  	}
   376  }
   377  
   378  func (valSet *defaultSet) Policy() istanbul.ProposerPolicy { return valSet.policy }
   379  
   380  func (valSet *defaultSet) Refresh(hash common.Hash, blockNum uint64, config *params.ChainConfig, isSingle bool, governingNode common.Address, minStaking uint64) error {
   381  	return nil
   382  }
   383  func (valSet *defaultSet) SetBlockNum(blockNum uint64)     { /* Do nothing */ }
   384  func (valSet *defaultSet) SetMixHash(mixHash []byte)       { /* Do nothing */ }
   385  func (valSet *defaultSet) Proposers() []istanbul.Validator { return nil }
   386  func (valSet *defaultSet) TotalVotingPower() uint64 {
   387  	sum := uint64(0)
   388  	for _, v := range valSet.List() {
   389  		sum += v.VotingPower()
   390  	}
   391  	return sum
   392  }
   393  
   394  func (valSet *defaultSet) Selector(valS istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator {
   395  	return valSet.selector(valS, lastProposer, round)
   396  }