github.com/iotexproject/iotex-core@v1.14.1-rc1/state/candidate.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 state 7 8 import ( 9 "math/big" 10 "strings" 11 12 "github.com/iotexproject/go-pkgs/hash" 13 "github.com/iotexproject/iotex-proto/golang/iotextypes" 14 "github.com/pkg/errors" 15 "google.golang.org/protobuf/proto" 16 17 "github.com/iotexproject/iotex-address/address" 18 ) 19 20 var ( 21 // ErrCandidate indicates the error of candidate 22 ErrCandidate = errors.New("invalid candidate") 23 // ErrCandidatePb indicates the error of protobuf's candidate message 24 ErrCandidatePb = errors.New("invalid protobuf's candidate message") 25 // ErrCandidateMap indicates the error of candidate map 26 ErrCandidateMap = errors.New("invalid candidate map") 27 // ErrCandidateList indicates the error of candidate list 28 ErrCandidateList = errors.New("invalid candidate list") 29 ) 30 31 type ( 32 // Candidate indicates the structure of a candidate 33 Candidate struct { 34 Address string 35 Votes *big.Int 36 RewardAddress string 37 CanName []byte // used as identifier to merge with native staking result, not part of protobuf 38 } 39 40 // CandidateList indicates the list of Candidates which is sortable 41 CandidateList []*Candidate 42 43 // CandidateMap is a map of Candidates using Hash160 as key 44 CandidateMap map[hash.Hash160]*Candidate 45 ) 46 47 // Equal compares two candidate instances 48 func (c *Candidate) Equal(d *Candidate) bool { 49 if c == d { 50 return true 51 } 52 if c == nil || d == nil { 53 return false 54 } 55 return strings.Compare(c.Address, d.Address) == 0 && 56 c.RewardAddress == d.RewardAddress && 57 c.Votes.Cmp(d.Votes) == 0 58 } 59 60 // Clone makes a copy of the candidate 61 func (c *Candidate) Clone() *Candidate { 62 if c == nil { 63 return nil 64 } 65 name := make([]byte, len(c.CanName)) 66 copy(name, c.CanName) 67 return &Candidate{ 68 Address: c.Address, 69 Votes: new(big.Int).Set(c.Votes), 70 RewardAddress: c.RewardAddress, 71 CanName: name, 72 } 73 } 74 75 // Serialize serializes candidate to bytes 76 func (c *Candidate) Serialize() ([]byte, error) { 77 return proto.Marshal(candidateToPb(c)) 78 } 79 80 // Deserialize deserializes bytes to candidate 81 func (c *Candidate) Deserialize(buf []byte) error { 82 pb := &iotextypes.Candidate{} 83 if err := proto.Unmarshal(buf, pb); err != nil { 84 return errors.Wrap(err, "failed to unmarshal candidate") 85 } 86 87 cand, err := pbToCandidate(pb) 88 if err != nil { 89 return errors.Wrap(err, "failed to convert protobuf's candidate message to candidate") 90 } 91 *c = *cand 92 93 return nil 94 } 95 96 func (l CandidateList) Len() int { return len(l) } 97 func (l CandidateList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 98 func (l CandidateList) Less(i, j int) bool { 99 if res := l[i].Votes.Cmp(l[j].Votes); res != 0 { 100 return res == 1 101 } 102 return strings.Compare(l[i].Address, l[j].Address) == 1 103 } 104 105 // Serialize serializes a list of Candidates to bytes 106 func (l *CandidateList) Serialize() ([]byte, error) { 107 return proto.Marshal(l.Proto()) 108 } 109 110 // Proto converts the candidate list to a protobuf message 111 func (l *CandidateList) Proto() *iotextypes.CandidateList { 112 candidatesPb := make([]*iotextypes.Candidate, 0, len(*l)) 113 for _, cand := range *l { 114 candidatesPb = append(candidatesPb, candidateToPb(cand)) 115 } 116 return &iotextypes.CandidateList{Candidates: candidatesPb} 117 } 118 119 // Deserialize deserializes bytes to list of Candidates 120 func (l *CandidateList) Deserialize(buf []byte) error { 121 candList := &iotextypes.CandidateList{} 122 if err := proto.Unmarshal(buf, candList); err != nil { 123 return errors.Wrap(err, "failed to unmarshal candidate list") 124 } 125 return l.LoadProto(candList) 126 } 127 128 // LoadProto loads candidate list from proto 129 func (l *CandidateList) LoadProto(candList *iotextypes.CandidateList) error { 130 candidates := make(CandidateList, 0) 131 candidatesPb := candList.Candidates 132 for _, candPb := range candidatesPb { 133 cand, err := pbToCandidate(candPb) 134 if err != nil { 135 return errors.Wrap(err, "failed to convert protobuf's candidate message to candidate") 136 } 137 candidates = append(candidates, cand) 138 } 139 *l = candidates 140 141 return nil 142 } 143 144 // candidateToPb converts a candidate to protobuf's candidate message 145 func candidateToPb(cand *Candidate) *iotextypes.Candidate { 146 candidatePb := &iotextypes.Candidate{ 147 Address: cand.Address, 148 Votes: cand.Votes.Bytes(), 149 RewardAddress: cand.RewardAddress, 150 } 151 if cand.Votes != nil && len(cand.Votes.Bytes()) > 0 { 152 candidatePb.Votes = cand.Votes.Bytes() 153 } 154 return candidatePb 155 } 156 157 // pbToCandidate converts a protobuf's candidate message to candidate 158 func pbToCandidate(candPb *iotextypes.Candidate) (*Candidate, error) { 159 if candPb == nil { 160 return nil, errors.Wrap(ErrCandidatePb, "protobuf's candidate message cannot be nil") 161 } 162 candidate := &Candidate{ 163 Address: candPb.Address, 164 Votes: big.NewInt(0).SetBytes(candPb.Votes), 165 RewardAddress: candPb.RewardAddress, 166 } 167 return candidate, nil 168 } 169 170 // MapToCandidates converts a map of cachedCandidates to candidate list 171 func MapToCandidates(candidateMap CandidateMap) (CandidateList, error) { 172 candidates := make(CandidateList, 0, len(candidateMap)) 173 for _, cand := range candidateMap { 174 candidates = append(candidates, cand) 175 } 176 return candidates, nil 177 } 178 179 // CandidatesToMap converts a candidate list to map of cachedCandidates 180 func CandidatesToMap(candidates CandidateList) (CandidateMap, error) { 181 candidateMap := make(CandidateMap) 182 for _, candidate := range candidates { 183 if candidate == nil { 184 return nil, errors.Wrap(ErrCandidate, "candidate cannot be nil") 185 } 186 addr, err := address.FromString(candidate.Address) 187 if err != nil { 188 return nil, errors.Wrap(err, "cannot get the hash of the address") 189 } 190 pkHash := hash.BytesToHash160(addr.Bytes()) 191 candidateMap[pkHash] = candidate 192 } 193 return candidateMap, nil 194 }