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 }