forked from FactomProject/factomd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
volunteercontrol.go
134 lines (110 loc) · 3.46 KB
/
volunteercontrol.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
130
131
132
133
134
package volunteercontrol
import (
"math"
"fmt"
"github.com/FactomProject/factomd/electionsCore/messages"
. "github.com/FactomProject/factomd/electionsCore/primitives"
)
var _ = fmt.Println
// VolunteerControl will keep a record of all votes
// for a given volunteer and produce the best vote
// possible.
type VolunteerControl struct {
AuthSet
Self Identity
Volunteer *messages.VolunteerMessage
Votes map[Identity]messages.LeaderLevelMessage
}
func NewVolunteerControl(self Identity, authset AuthSet) *VolunteerControl {
v := new(VolunteerControl)
v.Votes = make(map[Identity]messages.LeaderLevelMessage)
v.Self = self
v.AuthSet = authset
return v
}
// addVote just adds the vote to the vote map, and will not act upon it
func (v *VolunteerControl) AddVote(msg messages.LeaderLevelMessage) bool {
if v.Volunteer == nil {
v.Volunteer = &msg.VolunteerMessage
}
// If we already have a vote from that leader for this audit, then we only replace ours if this is better
if cur, ok := v.Votes[msg.Signer]; ok {
if cur.Level == msg.Level {
// Same level, same message (we have no malicious actors)
return false
}
if msg.Rank > cur.Rank {
// Greater rank is always better. Replace their current with the new
msg.Justification = nil
v.Votes[msg.Signer] = msg
return true
} else {
return false
}
}
v.Votes[msg.Signer] = msg
// New Vote, if we have more than a majority, delete the lowest vote
// to keep the majority the best majority possible
if len(v.Votes) > v.Majority() {
// Delete the lowest one, we don't need it
lowest := math.MaxInt32
var lowestvote messages.LeaderLevelMessage
lowestvote.Rank = math.MaxInt32
remove := NewIdentityFromInt(-1)
for k, v := range v.Votes {
if v.Level < lowest || (v.Level == lowest && !v.Less(&lowestvote)) {
// If level is lower OR equal and less
lowest = v.Level
remove = k
lowestvote = v
}
}
delete(v.Votes, remove)
}
return true
}
// checkVoteCount will check to see if we have enough votes to issue a ranked message. We will not add
// that message to our votemap, as we may have not chosen to actually send that vote. If we decide to send that
// vote, we will get it sent back to us
// Returns a LeaderLevelMessage with the level set, however it may need adjusting! (Can only adjust it up)
func (v *VolunteerControl) CheckVoteCount() *messages.LeaderLevelMessage {
// No majority, no bueno.
if len(v.Votes) < v.Majority() {
return nil
}
var justification []messages.LeaderLevelMessage
// Majority votes exist, we need to find the lowest level, and use it for our rank.
rank := math.MaxInt32
highestlevel := 0
for _, vote := range v.Votes {
// If vote level is less than current rank, bring down our rank
if vote.Level < rank {
rank = vote.Level
}
if vote.Level > highestlevel {
highestlevel = vote.Level
}
justification = append(justification, vote)
}
// Now we have the lowest level, any message at that level can no longer help us.
// We can only reuse votes at higher levels
for k, vote := range v.Votes {
if vote.Level <= rank {
delete(v.Votes, k)
}
}
llmsg := messages.NewLeaderLevelMessage(v.Self, rank, highestlevel, *v.Volunteer)
llmsg.Justification = justification
return &llmsg
}
func (a *VolunteerControl) Copy() *VolunteerControl {
b := NewVolunteerControl(a.Self, a.AuthSet.Copy())
if a.Volunteer != nil {
v := *a.Volunteer
b.Volunteer = &v
}
for k, v := range a.Votes {
b.Votes[k] = *v.Copy()
}
return b
}