github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/vote_reviser.go (about) 1 package staking 2 3 import ( 4 "math/big" 5 "sort" 6 7 "github.com/pkg/errors" 8 "go.uber.org/zap" 9 10 "github.com/iotexproject/iotex-core/blockchain/genesis" 11 "github.com/iotexproject/iotex-core/pkg/log" 12 "github.com/iotexproject/iotex-core/state" 13 ) 14 15 // VoteReviser is used to recalculate candidate votes. 16 type VoteReviser struct { 17 reviseHeights []uint64 18 cache map[uint64]CandidateList 19 c genesis.VoteWeightCalConsts 20 correctCandsHeight uint64 21 } 22 23 // NewVoteReviser creates a VoteReviser. 24 func NewVoteReviser(c genesis.VoteWeightCalConsts, correctCandsHeight uint64, reviseHeights ...uint64) *VoteReviser { 25 return &VoteReviser{ 26 reviseHeights: reviseHeights, 27 cache: make(map[uint64]CandidateList), 28 c: c, 29 correctCandsHeight: correctCandsHeight, 30 } 31 } 32 33 // Revise recalculate candidate votes on preset revising height. 34 func (vr *VoteReviser) Revise(csm CandidateStateManager, height uint64) error { 35 if !vr.isCacheExist(height) { 36 cands, err := vr.calculateVoteWeight(csm) 37 if err != nil { 38 return err 39 } 40 sort.Sort(cands) 41 if vr.needCorrectCands(height) { 42 cands, err = vr.correctAliasCands(csm, cands) 43 if err != nil { 44 return err 45 } 46 } 47 vr.storeToCache(height, cands) 48 } 49 return vr.flush(height, csm) 50 } 51 52 func (vr *VoteReviser) correctAliasCands(csm CandidateStateManager, cands CandidateList) (CandidateList, error) { 53 var retval CandidateList 54 for _, c := range csm.DirtyView().candCenter.base.nameMap { 55 retval = append(retval, c) 56 } 57 for _, c := range csm.DirtyView().candCenter.base.operatorMap { 58 retval = append(retval, c) 59 } 60 sort.Sort(retval) 61 ownerMap := map[string]*Candidate{} 62 for _, cand := range csm.DirtyView().candCenter.base.owners { 63 ownerMap[cand.Owner.String()] = cand 64 } 65 for _, c := range cands { 66 if owner, ok := ownerMap[c.Owner.String()]; ok { 67 c.Operator = owner.Operator 68 c.Reward = owner.Reward 69 c.Name = owner.Name 70 } 71 retval = append(retval, c) 72 } 73 return retval, nil 74 } 75 76 func (vr *VoteReviser) result(height uint64) (CandidateList, bool) { 77 cands, ok := vr.cache[height] 78 if !ok { 79 return nil, false 80 } 81 return cands, true 82 } 83 84 func (vr *VoteReviser) storeToCache(height uint64, cands CandidateList) { 85 vr.cache[height] = cands 86 } 87 88 func (vr *VoteReviser) isCacheExist(height uint64) bool { 89 _, ok := vr.cache[height] 90 return ok 91 } 92 93 // NeedRevise returns true if height needs revise 94 func (vr *VoteReviser) NeedRevise(height uint64) bool { 95 for _, h := range vr.reviseHeights { 96 if height == h { 97 return true 98 } 99 } 100 return vr.needCorrectCands(height) 101 } 102 103 // NeedCorrectCands returns true if height needs to correct candidates 104 func (vr *VoteReviser) needCorrectCands(height uint64) bool { 105 return height == vr.correctCandsHeight 106 } 107 108 func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager) (CandidateList, error) { 109 csr := newCandidateStateReader(csm.SM()) 110 cands, _, err := csr.getAllCandidates() 111 switch { 112 case errors.Cause(err) == state.ErrStateNotExist: 113 case err != nil: 114 return nil, err 115 } 116 candm := make(map[string]*Candidate) 117 for _, cand := range cands { 118 candm[cand.Owner.String()] = cand.Clone() 119 candm[cand.Owner.String()].Votes = new(big.Int) 120 candm[cand.Owner.String()].SelfStake = new(big.Int) 121 } 122 buckets, _, err := csr.getAllBuckets() 123 switch { 124 case errors.Cause(err) == state.ErrStateNotExist: 125 case err != nil: 126 return nil, err 127 } 128 129 for _, bucket := range buckets { 130 if bucket.isUnstaked() { 131 continue 132 } 133 cand, ok := candm[bucket.Candidate.String()] 134 if !ok { 135 log.L().Error("invalid bucket candidate", zap.Uint64("bucket index", bucket.Index), zap.String("candidate", bucket.Candidate.String())) 136 continue 137 } 138 139 if cand.SelfStakeBucketIdx == bucket.Index { 140 if err = cand.AddVote(CalculateVoteWeight(vr.c, bucket, true)); err != nil { 141 log.L().Error("failed to add vote for candidate", 142 zap.Uint64("bucket index", bucket.Index), 143 zap.String("candidate", bucket.Candidate.String()), 144 zap.Error(err)) 145 continue 146 } 147 cand.SelfStake = bucket.StakedAmount 148 } else { 149 _ = cand.AddVote(CalculateVoteWeight(vr.c, bucket, false)) 150 } 151 } 152 153 cands = make(CandidateList, 0, len(candm)) 154 for _, cand := range candm { 155 cands = append(cands, cand) 156 } 157 return cands, nil 158 } 159 160 func (vr *VoteReviser) flush(height uint64, csm CandidateStateManager) error { 161 cands, ok := vr.cache[height] 162 if !ok { 163 return nil 164 } 165 log.L().Info("committed revise action", 166 zap.Uint64("height", height), zap.Int("number of cands", len(cands))) 167 for _, cand := range cands { 168 if err := csm.Upsert(cand); err != nil { 169 return err 170 } 171 log.L().Info("committed revise action", 172 zap.String("name", cand.Name), zap.String("votes", cand.Votes.String())) 173 } 174 return nil 175 }