github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/candidate.go (about) 1 // Copyright (c) 2020 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 staking 7 8 import ( 9 "math/big" 10 "sort" 11 "strings" 12 13 "github.com/iotexproject/iotex-address/address" 14 "github.com/iotexproject/iotex-proto/golang/iotextypes" 15 "github.com/pkg/errors" 16 "google.golang.org/protobuf/proto" 17 18 "github.com/iotexproject/iotex-core/action" 19 "github.com/iotexproject/iotex-core/action/protocol/staking/stakingpb" 20 "github.com/iotexproject/iotex-core/state" 21 ) 22 23 type ( 24 // Candidate represents the candidate 25 Candidate struct { 26 Owner address.Address 27 Operator address.Address 28 Reward address.Address 29 Name string 30 Votes *big.Int 31 SelfStakeBucketIdx uint64 32 SelfStake *big.Int 33 } 34 35 // CandidateList is a list of candidates which is sortable 36 CandidateList []*Candidate 37 38 // RegistrationConsts are the registration fee and min self stake 39 RegistrationConsts struct { 40 Fee *big.Int 41 MinSelfStake *big.Int 42 } 43 ) 44 45 // Clone returns a copy 46 func (d *Candidate) Clone() *Candidate { 47 return &Candidate{ 48 Owner: d.Owner, 49 Operator: d.Operator, 50 Reward: d.Reward, 51 Name: d.Name, 52 Votes: new(big.Int).Set(d.Votes), 53 SelfStakeBucketIdx: d.SelfStakeBucketIdx, 54 SelfStake: new(big.Int).Set(d.SelfStake), 55 } 56 } 57 58 // Equal tests equality of 2 candidates 59 func (d *Candidate) Equal(c *Candidate) bool { 60 return d.Name == c.Name && 61 d.SelfStakeBucketIdx == c.SelfStakeBucketIdx && 62 address.Equal(d.Owner, c.Owner) && 63 address.Equal(d.Operator, c.Operator) && 64 address.Equal(d.Reward, c.Reward) && 65 d.Votes.Cmp(c.Votes) == 0 && 66 d.SelfStake.Cmp(c.SelfStake) == 0 67 } 68 69 // Validate does the sanity check 70 func (d *Candidate) Validate() error { 71 if d.Votes == nil { 72 return action.ErrInvalidAmount 73 } 74 75 if d.Name == "" { 76 return action.ErrInvalidCanName 77 } 78 79 if d.Owner == nil { 80 return ErrInvalidOwner 81 } 82 83 if d.Operator == nil { 84 return ErrInvalidOperator 85 } 86 87 if d.Reward == nil { 88 return ErrInvalidReward 89 } 90 91 if d.SelfStake == nil { 92 return action.ErrInvalidAmount 93 } 94 return nil 95 } 96 97 // isSelfStakeBucketSettled checks if self stake bucket is settled 98 func (d *Candidate) isSelfStakeBucketSettled() bool { 99 return d.SelfStakeBucketIdx != candidateNoSelfStakeBucketIndex 100 } 101 102 // Collision checks collsion of 2 candidates 103 func (d *Candidate) Collision(c *Candidate) error { 104 if address.Equal(d.Owner, c.Owner) { 105 return nil 106 } 107 if c.Name == d.Name { 108 return action.ErrInvalidCanName 109 } 110 if address.Equal(c.Operator, d.Operator) { 111 return ErrInvalidOperator 112 } 113 if c.SelfStakeBucketIdx == d.SelfStakeBucketIdx && c.isSelfStakeBucketSettled() { 114 return ErrInvalidSelfStkIndex 115 } 116 return nil 117 } 118 119 // AddVote adds vote 120 func (d *Candidate) AddVote(amount *big.Int) error { 121 if amount.Sign() < 0 { 122 return action.ErrInvalidAmount 123 } 124 d.Votes.Add(d.Votes, amount) 125 return nil 126 } 127 128 // SubVote subtracts vote 129 func (d *Candidate) SubVote(amount *big.Int) error { 130 if amount.Sign() < 0 { 131 return action.ErrInvalidAmount 132 } 133 134 if d.Votes.Cmp(amount) == -1 { 135 return action.ErrInvalidAmount 136 } 137 d.Votes.Sub(d.Votes, amount) 138 return nil 139 } 140 141 // AddSelfStake adds self stake 142 func (d *Candidate) AddSelfStake(amount *big.Int) error { 143 if amount.Sign() < 0 { 144 return action.ErrInvalidAmount 145 } 146 d.SelfStake.Add(d.SelfStake, amount) 147 return nil 148 } 149 150 // SubSelfStake subtracts self stake 151 func (d *Candidate) SubSelfStake(amount *big.Int) error { 152 if amount.Sign() < 0 { 153 return action.ErrInvalidAmount 154 } 155 156 if d.Votes.Cmp(amount) == -1 { 157 return action.ErrInvalidAmount 158 } 159 d.SelfStake.Sub(d.SelfStake, amount) 160 return nil 161 } 162 163 // Serialize serializes candidate to bytes 164 func (d *Candidate) Serialize() ([]byte, error) { 165 pb, err := d.toProto() 166 if err != nil { 167 return nil, err 168 } 169 return proto.Marshal(pb) 170 } 171 172 // Deserialize deserializes bytes to candidate 173 func (d *Candidate) Deserialize(buf []byte) error { 174 pb := &stakingpb.Candidate{} 175 if err := proto.Unmarshal(buf, pb); err != nil { 176 return errors.Wrap(err, "failed to unmarshal candidate") 177 } 178 return d.fromProto(pb) 179 } 180 181 func (d *Candidate) toProto() (*stakingpb.Candidate, error) { 182 if d.Owner == nil || d.Operator == nil || d.Reward == nil || 183 len(d.Name) == 0 || d.Votes == nil || d.SelfStake == nil { 184 return nil, ErrMissingField 185 } 186 187 return &stakingpb.Candidate{ 188 OwnerAddress: d.Owner.String(), 189 OperatorAddress: d.Operator.String(), 190 RewardAddress: d.Reward.String(), 191 Name: d.Name, 192 Votes: d.Votes.String(), 193 SelfStakeBucketIdx: d.SelfStakeBucketIdx, 194 SelfStake: d.SelfStake.String(), 195 }, nil 196 } 197 198 func (d *Candidate) fromProto(pb *stakingpb.Candidate) error { 199 var err error 200 d.Owner, err = address.FromString(pb.GetOwnerAddress()) 201 if err != nil { 202 return err 203 } 204 205 d.Operator, err = address.FromString(pb.GetOperatorAddress()) 206 if err != nil { 207 return err 208 } 209 210 d.Reward, err = address.FromString(pb.GetRewardAddress()) 211 if err != nil { 212 return err 213 } 214 215 if len(pb.GetName()) == 0 { 216 return ErrMissingField 217 } 218 d.Name = pb.GetName() 219 220 var ok bool 221 d.Votes, ok = new(big.Int).SetString(pb.GetVotes(), 10) 222 if !ok { 223 return action.ErrInvalidAmount 224 } 225 226 d.SelfStakeBucketIdx = pb.GetSelfStakeBucketIdx() 227 d.SelfStake, ok = new(big.Int).SetString(pb.GetSelfStake(), 10) 228 if !ok { 229 return action.ErrInvalidAmount 230 } 231 return nil 232 } 233 234 func (d *Candidate) toIoTeXTypes() *iotextypes.CandidateV2 { 235 return &iotextypes.CandidateV2{ 236 OwnerAddress: d.Owner.String(), 237 OperatorAddress: d.Operator.String(), 238 RewardAddress: d.Reward.String(), 239 Name: d.Name, 240 TotalWeightedVotes: d.Votes.String(), 241 SelfStakeBucketIdx: d.SelfStakeBucketIdx, 242 SelfStakingTokens: d.SelfStake.String(), 243 } 244 } 245 246 func (d *Candidate) toStateCandidate() *state.Candidate { 247 return &state.Candidate{ 248 Address: d.Operator.String(), // state need candidate operator not owner address 249 Votes: new(big.Int).Set(d.Votes), 250 RewardAddress: d.Reward.String(), 251 CanName: []byte(d.Name), 252 } 253 } 254 255 func (l CandidateList) Len() int { return len(l) } 256 func (l CandidateList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 257 func (l CandidateList) Less(i, j int) bool { 258 if res := l[i].Votes.Cmp(l[j].Votes); res != 0 { 259 return res == 1 260 } 261 if res := strings.Compare(l[i].Owner.String(), l[j].Owner.String()); res != 0 { 262 return res == 1 263 } 264 if res := strings.Compare(l[i].Reward.String(), l[j].Reward.String()); res != 0 { 265 return res == 1 266 } 267 if res := strings.Compare(l[i].Operator.String(), l[j].Operator.String()); res != 0 { 268 return res == 1 269 } 270 switch { 271 case l[i].SelfStakeBucketIdx > l[j].SelfStakeBucketIdx: 272 return true 273 case l[i].SelfStakeBucketIdx < l[j].SelfStakeBucketIdx: 274 return false 275 } 276 if res := l[i].SelfStake.Cmp(l[j].SelfStake); res != 0 { 277 return res == 1 278 } 279 return strings.Compare(l[i].Name, l[j].Name) == 1 280 } 281 282 // Serialize serializes candidate to bytes 283 func (l CandidateList) Serialize() ([]byte, error) { 284 pb, err := l.toProto() 285 if err != nil { 286 return nil, err 287 } 288 return proto.Marshal(pb) 289 } 290 291 func (l CandidateList) toProto() (*stakingpb.Candidates, error) { 292 candidatePb := make([]*stakingpb.Candidate, len(l)) 293 for i, del := range l { 294 dpb, err := del.toProto() 295 if err != nil { 296 return nil, err 297 } 298 candidatePb[i] = dpb 299 } 300 return &stakingpb.Candidates{Candidates: candidatePb}, nil 301 } 302 303 // Deserialize deserializes bytes to list of candidates 304 func (l *CandidateList) Deserialize(buf []byte) error { 305 pb := &stakingpb.Candidates{} 306 if err := proto.Unmarshal(buf, pb); err != nil { 307 return errors.Wrap(err, "failed to unmarshal candidate list") 308 } 309 310 *l = (*l)[:0] 311 for _, v := range pb.Candidates { 312 c := &Candidate{} 313 if err := c.fromProto(v); err != nil { 314 return err 315 } 316 *l = append(*l, c) 317 } 318 return nil 319 } 320 321 func (l CandidateList) toStateCandidateList() (state.CandidateList, error) { 322 list := make(state.CandidateList, 0, len(l)) 323 for _, c := range l { 324 list = append(list, c.toStateCandidate()) 325 } 326 sort.Sort(list) 327 return list, nil 328 }