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  }