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  }