github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/validator/default.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package validator 18 19 import ( 20 "math" 21 "reflect" 22 "sync" 23 24 "github.com/electroneum/electroneum-sc/common" 25 "github.com/electroneum/electroneum-sc/consensus/istanbul" 26 ) 27 28 type defaultValidator struct { 29 address common.Address 30 } 31 32 func (val *defaultValidator) Address() common.Address { 33 return val.address 34 } 35 36 func (val *defaultValidator) String() string { 37 return val.Address().String() 38 } 39 40 // ---------------------------------------------------------------------------- 41 42 type defaultSet struct { 43 validators istanbul.Validators 44 policy *istanbul.ProposerPolicy 45 46 proposer istanbul.Validator 47 validatorMu sync.RWMutex 48 selector istanbul.ProposalSelector 49 } 50 51 func newDefaultSet(addrs []common.Address, policy *istanbul.ProposerPolicy) *defaultSet { 52 valSet := &defaultSet{} 53 54 valSet.policy = policy 55 // init validators 56 valSet.validators = make([]istanbul.Validator, len(addrs)) 57 for i, addr := range addrs { 58 valSet.validators[i] = New(addr) 59 } 60 61 valSet.SortValidators() 62 // init proposer 63 if valSet.Size() > 0 { 64 valSet.proposer = valSet.GetByIndex(0) 65 } 66 valSet.selector = roundRobinProposer 67 if policy.Id == istanbul.Sticky { 68 valSet.selector = stickyProposer 69 } 70 71 policy.RegisterValidatorSet(valSet) 72 73 return valSet 74 } 75 76 func (valSet *defaultSet) Size() int { 77 valSet.validatorMu.RLock() 78 defer valSet.validatorMu.RUnlock() 79 return len(valSet.validators) 80 } 81 82 func (valSet *defaultSet) List() []istanbul.Validator { 83 valSet.validatorMu.RLock() 84 defer valSet.validatorMu.RUnlock() 85 return valSet.validators 86 } 87 88 func (valSet *defaultSet) GetByIndex(i uint64) istanbul.Validator { 89 valSet.validatorMu.RLock() 90 defer valSet.validatorMu.RUnlock() 91 if i < uint64(valSet.Size()) { 92 return valSet.validators[i] 93 } 94 return nil 95 } 96 97 func (valSet *defaultSet) GetByAddress(addr common.Address) (int, istanbul.Validator) { 98 for i, val := range valSet.List() { 99 if addr == val.Address() { 100 return i, val 101 } 102 } 103 return -1, nil 104 } 105 106 func (valSet *defaultSet) GetProposer() istanbul.Validator { 107 return valSet.proposer 108 } 109 110 func (valSet *defaultSet) IsProposer(address common.Address) bool { 111 _, val := valSet.GetByAddress(address) 112 return reflect.DeepEqual(valSet.GetProposer(), val) 113 } 114 115 func (valSet *defaultSet) CalcProposer(lastProposer common.Address, round uint64) { 116 valSet.validatorMu.RLock() 117 defer valSet.validatorMu.RUnlock() 118 valSet.proposer = valSet.selector(valSet, lastProposer, round) 119 } 120 121 // ValidatorSetSorter sorts the validators based on the configured By function 122 func (valSet *defaultSet) SortValidators() { 123 valSet.Policy().By.Sort(valSet.validators) 124 } 125 126 func calcSeed(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) uint64 { 127 offset := 0 128 if idx, val := valSet.GetByAddress(proposer); val != nil { 129 offset = idx 130 } 131 return uint64(offset) + round 132 } 133 134 func emptyAddress(addr common.Address) bool { 135 return addr == common.Address{} 136 } 137 138 func roundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { 139 if valSet.Size() == 0 { 140 return nil 141 } 142 seed := uint64(0) 143 if emptyAddress(proposer) { 144 seed = round 145 } else { 146 seed = calcSeed(valSet, proposer, round) + 1 147 } 148 pick := seed % uint64(valSet.Size()) 149 return valSet.GetByIndex(pick) 150 } 151 152 func stickyProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { 153 if valSet.Size() == 0 { 154 return nil 155 } 156 seed := uint64(0) 157 if emptyAddress(proposer) { 158 seed = round 159 } else { 160 seed = calcSeed(valSet, proposer, round) 161 } 162 pick := seed % uint64(valSet.Size()) 163 return valSet.GetByIndex(pick) 164 } 165 166 func (valSet *defaultSet) AddValidator(address common.Address) bool { 167 valSet.validatorMu.Lock() 168 defer valSet.validatorMu.Unlock() 169 for _, v := range valSet.validators { 170 if v.Address() == address { 171 return false 172 } 173 } 174 valSet.validators = append(valSet.validators, New(address)) 175 // TODO: we may not need to re-sort it again 176 // sort validator 177 valSet.SortValidators() 178 return true 179 } 180 181 func (valSet *defaultSet) RemoveValidator(address common.Address) bool { 182 valSet.validatorMu.Lock() 183 defer valSet.validatorMu.Unlock() 184 185 for i, v := range valSet.validators { 186 if v.Address() == address { 187 valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...) 188 return true 189 } 190 } 191 return false 192 } 193 194 func (valSet *defaultSet) Copy() istanbul.ValidatorSet { 195 valSet.validatorMu.RLock() 196 defer valSet.validatorMu.RUnlock() 197 198 addresses := make([]common.Address, 0, len(valSet.validators)) 199 for _, v := range valSet.validators { 200 addresses = append(addresses, v.Address()) 201 } 202 return NewSet(addresses, valSet.policy) 203 } 204 205 func (valSet *defaultSet) F() int { return int(math.Ceil(float64(valSet.Size())/3)) - 1 } 206 207 func (valSet *defaultSet) Policy() istanbul.ProposerPolicy { return *valSet.policy }