github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/vote/candidatesutil/candidatesutil.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package candidatesutil 7 8 import ( 9 "math/big" 10 "sort" 11 12 "go.uber.org/zap" 13 14 "github.com/pkg/errors" 15 16 "github.com/iotexproject/go-pkgs/hash" 17 "github.com/iotexproject/iotex-address/address" 18 "github.com/iotexproject/iotex-core/action/protocol" 19 "github.com/iotexproject/iotex-core/action/protocol/vote" 20 "github.com/iotexproject/iotex-core/pkg/log" 21 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 22 "github.com/iotexproject/iotex-core/state" 23 ) 24 25 // CandidatesPrefix is the prefix of the key of candidateList 26 const CandidatesPrefix = "Candidates." 27 28 // CurCandidateKey is the key of current candidate list 29 const CurCandidateKey = "CurrentCandidateList." 30 31 // NxtCandidateKey is the key of next candidate list 32 const NxtCandidateKey = "NextCandidateList." 33 34 // CurProbationKey is the key of current probation list 35 const CurProbationKey = "CurrentKickoutKey." 36 37 // NxtProbationKey is the key of next probation list 38 const NxtProbationKey = "NextKickoutKey." 39 40 // UnproductiveDelegateKey is the key of unproductive Delegate struct 41 const UnproductiveDelegateKey = "UnproductiveDelegateKey." 42 43 // CandidatesFromDB returns array of Candidates in candidate pool of a given height or current epoch 44 func CandidatesFromDB(sr protocol.StateReader, height uint64, loadCandidatesLegacy bool, epochStartPoint bool) ([]*state.Candidate, uint64, error) { 45 var candidates state.CandidateList 46 var stateHeight uint64 47 var err error 48 if loadCandidatesLegacy { 49 // Load Candidates on the given height from underlying db [deprecated] 50 candidatesKey := ConstructLegacyKey(height) 51 stateHeight, err = sr.State(&candidates, protocol.LegacyKeyOption(candidatesKey)) 52 } else { 53 candidatesKey := ConstructKey(CurCandidateKey) 54 if epochStartPoint { 55 // if not shifted yet 56 log.L().Debug("Read candidate list with next candidate key", zap.Uint64("height", height)) 57 candidatesKey = ConstructKey(NxtCandidateKey) 58 } 59 stateHeight, err = sr.State( 60 &candidates, 61 protocol.KeyOption(candidatesKey[:]), 62 protocol.NamespaceOption(protocol.SystemNamespace), 63 ) 64 } 65 log.L().Debug( 66 "CandidatesFromDB", 67 zap.Bool("loadCandidatesLegacy", loadCandidatesLegacy), 68 zap.Uint64("height", height), 69 zap.Uint64("stateHeight", stateHeight), 70 zap.Any("candidates", candidates), 71 zap.Error(err), 72 ) 73 if errors.Cause(err) == nil { 74 if len(candidates) > 0 { 75 return candidates, stateHeight, nil 76 } 77 err = state.ErrStateNotExist 78 } 79 return nil, stateHeight, errors.Wrapf( 80 err, 81 "failed to get candidates with epochStartPoint: %t", 82 epochStartPoint, 83 ) 84 } 85 86 // ProbationListFromDB returns array of probation list at current epoch 87 func ProbationListFromDB(sr protocol.StateReader, epochStartPoint bool) (*vote.ProbationList, uint64, error) { 88 probationList := &vote.ProbationList{} 89 probationlistKey := ConstructKey(CurProbationKey) 90 if epochStartPoint { 91 // if not shifted yet 92 log.L().Debug("Read probation list with next probation key") 93 probationlistKey = ConstructKey(NxtProbationKey) 94 } 95 stateHeight, err := sr.State( 96 probationList, 97 protocol.KeyOption(probationlistKey[:]), 98 protocol.NamespaceOption(protocol.SystemNamespace), 99 ) 100 log.L().Debug( 101 "GetProbationList", 102 zap.Any("Probation list", probationList.ProbationInfo), 103 zap.Uint64("state height", stateHeight), 104 zap.Error(err), 105 ) 106 if err == nil { 107 return probationList, stateHeight, nil 108 } 109 return nil, stateHeight, errors.Wrapf( 110 err, 111 "failed to get probation list with epochStartPoint: %t", 112 epochStartPoint, 113 ) 114 } 115 116 // UnproductiveDelegateFromDB returns latest UnproductiveDelegate struct 117 func UnproductiveDelegateFromDB(sr protocol.StateReader) (*vote.UnproductiveDelegate, error) { 118 upd := &vote.UnproductiveDelegate{} 119 updKey := ConstructKey(UnproductiveDelegateKey) 120 stateHeight, err := sr.State( 121 upd, 122 protocol.KeyOption(updKey[:]), 123 protocol.NamespaceOption(protocol.SystemNamespace), 124 ) 125 log.L().Debug( 126 "GetUnproductiveDelegate", 127 zap.Uint64("state height", stateHeight), 128 zap.Error(err), 129 ) 130 if err == nil { 131 return upd, nil 132 } 133 return nil, err 134 } 135 136 // ConstructLegacyKey constructs a key for candidates storage (deprecated version) 137 func ConstructLegacyKey(height uint64) hash.Hash160 { 138 heightInBytes := byteutil.Uint64ToBytes(height) 139 k := []byte(CandidatesPrefix) 140 k = append(k, heightInBytes...) 141 return hash.Hash160b(k) 142 } 143 144 // ConstructKey constructs a const key 145 func ConstructKey(key string) hash.Hash256 { 146 bytesKey := []byte(key) 147 return hash.Hash256b(bytesKey) 148 } 149 150 // LoadAndAddCandidates loads candidates from trie and adds a new candidate 151 func LoadAndAddCandidates(sm protocol.StateManager, blkHeight uint64, addr string) error { 152 candidateMap, err := GetMostRecentCandidateMap(sm, blkHeight) 153 if err != nil { 154 return errors.Wrap(err, "failed to get most recent candidates from trie") 155 } 156 if err := addCandidate(candidateMap, addr); err != nil { 157 return errors.Wrap(err, "failed to add candidate to candidate map") 158 } 159 return storeCandidates(candidateMap, sm, blkHeight) 160 } 161 162 // GetMostRecentCandidateMap gets the most recent candidateMap from trie 163 func GetMostRecentCandidateMap(sm protocol.StateManager, blkHeight uint64) (map[hash.Hash160]*state.Candidate, error) { 164 var sc state.CandidateList 165 for h := int(blkHeight); h >= 0; h-- { 166 candidatesKey := ConstructLegacyKey(uint64(h)) 167 var err error 168 if _, err = sm.State(&sc, protocol.LegacyKeyOption(candidatesKey)); err == nil { 169 return state.CandidatesToMap(sc) 170 } 171 if errors.Cause(err) != state.ErrStateNotExist { 172 return nil, errors.Wrap(err, "failed to get most recent state of candidateList") 173 } 174 } 175 if blkHeight == uint64(0) || blkHeight == uint64(1) { 176 return make(map[hash.Hash160]*state.Candidate), nil 177 } 178 return nil, errors.Wrap(state.ErrStateNotExist, "failed to get most recent state of candidateList") 179 } 180 181 // addCandidate adds a new candidate to candidateMap 182 func addCandidate(candidateMap map[hash.Hash160]*state.Candidate, encodedAddr string) error { 183 addr, err := address.FromString(encodedAddr) 184 if err != nil { 185 return errors.Wrap(err, "failed to get public key hash from account address") 186 } 187 addrHash := hash.BytesToHash160(addr.Bytes()) 188 if _, ok := candidateMap[addrHash]; !ok { 189 candidateMap[addrHash] = &state.Candidate{ 190 Address: encodedAddr, 191 Votes: big.NewInt(0), 192 } 193 } 194 return nil 195 } 196 197 // storeCandidates puts updated candidates to trie 198 func storeCandidates(candidateMap map[hash.Hash160]*state.Candidate, sm protocol.StateManager, blkHeight uint64) error { 199 candidateList, err := state.MapToCandidates(candidateMap) 200 if err != nil { 201 return errors.Wrap(err, "failed to convert candidate map to candidate list") 202 } 203 sort.Sort(candidateList) 204 candidatesKey := ConstructLegacyKey(blkHeight) 205 _, err = sm.PutState(&candidateList, protocol.LegacyKeyOption(candidatesKey)) 206 return err 207 }