-
Notifications
You must be signed in to change notification settings - Fork 320
/
vote_reviser.go
129 lines (114 loc) · 3.25 KB
/
vote_reviser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package staking
import (
"math/big"
"sort"
"github.com/pkg/errors"
"go.uber.org/zap"
"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/blockchain/genesis"
"github.com/iotexproject/iotex-core/pkg/log"
"github.com/iotexproject/iotex-core/state"
)
// VoteReviser is used to recalculate candidate votes.
type VoteReviser struct {
reviseHeights []uint64
cache map[uint64]CandidateList
c genesis.VoteWeightCalConsts
}
// NewVoteReviser creates a VoteReviser.
func NewVoteReviser(c genesis.VoteWeightCalConsts, reviseHeights ...uint64) *VoteReviser {
return &VoteReviser{
reviseHeights: reviseHeights,
cache: make(map[uint64]CandidateList),
c: c,
}
}
// Revise recalculate candidate votes on preset revising height.
func (vr *VoteReviser) Revise(csm CandidateStateManager, height uint64) error {
if !vr.isCacheExist(height) {
cands, err := vr.calculateVoteWeight(csm)
if err != nil {
return err
}
vr.storeToCache(height, cands)
}
return vr.flush(height, csm)
}
func (vr *VoteReviser) result(height uint64) (CandidateList, bool) {
cands, ok := vr.cache[height]
return cands, ok
}
func (vr *VoteReviser) storeToCache(height uint64, cands CandidateList) {
vr.cache[height] = cands
}
func (vr *VoteReviser) isCacheExist(height uint64) bool {
_, ok := vr.cache[height]
return ok
}
// NeedRevise returns true if height needs revise
func (vr *VoteReviser) NeedRevise(height uint64) bool {
for _, h := range vr.reviseHeights {
if height == h {
return true
}
}
return false
}
func (vr *VoteReviser) calculateVoteWeight(sm protocol.StateManager) (CandidateList, error) {
cands, _, err := getAllCandidates(sm)
switch {
case errors.Cause(err) == state.ErrStateNotExist:
case err != nil:
return nil, err
}
candm := make(map[string]*Candidate)
for _, cand := range cands {
candm[cand.Owner.String()] = cand.Clone()
candm[cand.Owner.String()].Votes = new(big.Int)
candm[cand.Owner.String()].SelfStake = new(big.Int)
}
buckets, _, err := getAllBuckets(sm)
switch {
case errors.Cause(err) == state.ErrStateNotExist:
case err != nil:
return nil, err
}
for _, bucket := range buckets {
if bucket.isUnstaked() {
continue
}
cand, ok := candm[bucket.Candidate.String()]
if !ok {
log.L().Error("invalid bucket candidate", zap.Uint64("bucket index", bucket.Index), zap.String("candidate", bucket.Candidate.String()))
continue
}
if cand.SelfStakeBucketIdx == bucket.Index {
cand.AddVote(calculateVoteWeight(vr.c, bucket, true))
cand.SelfStake = bucket.StakedAmount
} else {
cand.AddVote(calculateVoteWeight(vr.c, bucket, false))
}
}
cands = make(CandidateList, 0, len(candm))
for _, cand := range candm {
cands = append(cands, cand)
}
return cands, nil
}
func (vr *VoteReviser) flush(height uint64, csm CandidateStateManager) error {
cands, ok := vr.cache[height]
if !ok {
return nil
}
sort.Sort(cands)
log.L().Info("committed revise action",
zap.Uint64("height", height), zap.Int("number of cands", len(cands)))
for _, cand := range cands {
if err := csm.Upsert(cand); err != nil {
return err
}
log.L().Info("committed revise action",
zap.String("name", cand.Name), zap.String("votes", cand.Votes.String()))
}
return nil
}