github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/validator/validator.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/validator.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package validator
    22  
    23  import (
    24  	"math/rand"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/klaytn/klaytn/common"
    29  	"github.com/klaytn/klaytn/consensus"
    30  	"github.com/klaytn/klaytn/consensus/istanbul"
    31  	"github.com/klaytn/klaytn/log"
    32  )
    33  
    34  var logger = log.NewModuleLogger(log.ConsensusIstanbulValidator)
    35  
    36  func New(addr common.Address) istanbul.Validator {
    37  	return &defaultValidator{
    38  		address: addr,
    39  	}
    40  }
    41  
    42  func NewValidatorSet(addrs, demotedAddrs []common.Address, proposerPolicy istanbul.ProposerPolicy, subGroupSize uint64, chain consensus.ChainReader) istanbul.ValidatorSet {
    43  	var valSet istanbul.ValidatorSet
    44  	if proposerPolicy == istanbul.WeightedRandom {
    45  		valSet = NewWeightedCouncil(addrs, demotedAddrs, nil, nil, nil, proposerPolicy, subGroupSize, 0, 0, chain)
    46  	} else {
    47  		valSet = NewSubSet(addrs, proposerPolicy, subGroupSize)
    48  	}
    49  
    50  	return valSet
    51  }
    52  
    53  func NewSet(addrs []common.Address, policy istanbul.ProposerPolicy) istanbul.ValidatorSet {
    54  	return newDefaultSet(addrs, policy)
    55  }
    56  
    57  func NewSubSet(addrs []common.Address, policy istanbul.ProposerPolicy, subSize uint64) istanbul.ValidatorSet {
    58  	return newDefaultSubSet(addrs, policy, subSize)
    59  }
    60  
    61  func ExtractValidators(extraData []byte) []common.Address {
    62  	// get the validator addresses
    63  	addrs := make([]common.Address, (len(extraData) / common.AddressLength))
    64  	for i := 0; i < len(addrs); i++ {
    65  		copy(addrs[i][:], extraData[i*common.AddressLength:])
    66  	}
    67  
    68  	return addrs
    69  }
    70  
    71  // ConvertHashToSeed returns a random seed used to calculate proposer.
    72  // It converts first 7.5 bytes of the given hash to int64.
    73  func ConvertHashToSeed(hash common.Hash) (int64, error) {
    74  	// TODO-Klaytn-Istanbul: convert hash.Hex() to int64 directly without string conversion
    75  	hashstring := strings.TrimPrefix(hash.Hex(), "0x")
    76  	if len(hashstring) > 15 {
    77  		hashstring = hashstring[:15]
    78  	}
    79  
    80  	seed, err := strconv.ParseInt(hashstring, 16, 64)
    81  	if err != nil {
    82  		logger.Error("fail to make sub-list of validators", "hash", hash.Hex(), "seed", seed, "err", err)
    83  		return 0, err
    84  	}
    85  	return seed, nil
    86  }
    87  
    88  // SelectRandomCommittee composes a committee selecting validators randomly based on the seed value.
    89  // It returns nil if the given committeeSize is bigger than validatorSize or proposer indexes are invalid.
    90  func SelectRandomCommittee(validators []istanbul.Validator, committeeSize uint64, seed int64, proposerIdx int, nextProposerIdx int) []istanbul.Validator {
    91  	// ensure validator indexes are valid
    92  	if proposerIdx < 0 || nextProposerIdx < 0 || proposerIdx == nextProposerIdx {
    93  		logger.Error("invalid indexes of validators", "proposerIdx", proposerIdx, "nextProposerIdx", nextProposerIdx)
    94  		return nil
    95  	}
    96  
    97  	// ensure committeeSize and proposer indexes are valid
    98  	validatorSize := len(validators)
    99  	if validatorSize < int(committeeSize) || validatorSize <= proposerIdx || validatorSize <= nextProposerIdx {
   100  		logger.Error("invalid committee size or validator indexes", "validatorSize", validatorSize,
   101  			"committeeSize", committeeSize, "proposerIdx", proposerIdx, "nextProposerIdx", nextProposerIdx)
   102  		return nil
   103  	}
   104  
   105  	// it cannot be happened. just to make sure
   106  	if committeeSize < 2 {
   107  		if committeeSize == 0 {
   108  			logger.Error("committee size has an invalid value", "committeeSize", committeeSize)
   109  			return nil
   110  		}
   111  		return []istanbul.Validator{validators[proposerIdx]}
   112  	}
   113  
   114  	// first committee is the proposer and the second committee is the next proposer
   115  	committee := make([]istanbul.Validator, committeeSize)
   116  	committee[0] = validators[proposerIdx]
   117  	committee[1] = validators[nextProposerIdx]
   118  
   119  	// select the reset of committee members randomly
   120  	picker := rand.New(rand.NewSource(seed))
   121  	pickSize := validatorSize - 2
   122  	indexs := make([]int, pickSize)
   123  	idx := 0
   124  	for i := 0; i < validatorSize; i++ {
   125  		if i != proposerIdx && i != nextProposerIdx {
   126  			indexs[idx] = i
   127  			idx++
   128  		}
   129  	}
   130  
   131  	for i := 0; i < pickSize; i++ {
   132  		randIndex := picker.Intn(pickSize)
   133  		indexs[i], indexs[randIndex] = indexs[randIndex], indexs[i]
   134  	}
   135  
   136  	for i := uint64(0); i < committeeSize-2; i++ {
   137  		committee[i+2] = validators[indexs[i]]
   138  	}
   139  
   140  	return committee
   141  }