-
Notifications
You must be signed in to change notification settings - Fork 211
/
statustracker.go
103 lines (85 loc) · 2.78 KB
/
statustracker.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
package hare
import (
"github.com/spacemeshos/go-spacemesh/log"
)
// statusTracker tracks status messages.
// Provides functions to build a proposal and validate the statuses.
type statusTracker struct {
statuses map[string]*Msg // maps PubKey->StatusMsg
threshold int // threshold to indicate a set can be proved
maxKi int32 // tracks max Ki in tracked status Messages
maxSet *Set // tracks the max raw set in the tracked status Messages
analyzed bool // indicates if the Messages have already been analyzed
log.Log
}
func newStatusTracker(threshold int, expectedSize int) *statusTracker {
st := &statusTracker{}
st.statuses = make(map[string]*Msg, expectedSize)
st.threshold = threshold
st.maxKi = -1 // since Ki>=-1
st.maxSet = nil
st.analyzed = false
return st
}
// RecordStatus records the given status message
func (st *statusTracker) RecordStatus(msg *Msg) {
pub := msg.PubKey
_, exist := st.statuses[pub.String()]
if exist { // already handled this sender's status msg
st.Warning("Duplicated status message detected %v", pub.String())
return
}
st.statuses[pub.String()] = msg
}
// AnalyzeStatuses analyzes the recorded status messages by the validation function.
func (st *statusTracker) AnalyzeStatuses(isValid func(m *Msg) bool) {
count := 0
for key, m := range st.statuses {
if !isValid(m) || count == st.threshold { // only keep valid Messages
delete(st.statuses, key)
} else {
count++
if m.InnerMsg.Ki >= st.maxKi { // track max Ki & matching raw set
st.maxKi = m.InnerMsg.Ki
st.maxSet = NewSet(m.InnerMsg.Values)
}
}
}
st.analyzed = true
}
// IsSVPReady returns true if theere are enough statuses to build an SVP, false otherwise.
func (st *statusTracker) IsSVPReady() bool {
return st.analyzed && len(st.statuses) == st.threshold
}
// ProposalSet returns the proposed set if available, nil otherwise.
func (st *statusTracker) ProposalSet(expectedSize int) *Set {
if st.maxKi == -1 {
return st.buildUnionSet(expectedSize)
}
if st.maxSet == nil { // should be impossible to reach
panic("maxSet is unexpectedly nil")
}
return st.maxSet
}
// returns the union set of all status Messages collected
func (st *statusTracker) buildUnionSet(expectedSize int) *Set {
unionSet := NewEmptySet(expectedSize)
for _, m := range st.statuses {
for bid := range NewSet(m.InnerMsg.Values).values {
unionSet.Add(bid) // assuming add is unique
}
}
return unionSet
}
// BuildSVP builds the SVP if avilable and returns it, it return false otherwise.
func (st *statusTracker) BuildSVP() *aggregatedMessages {
if !st.IsSVPReady() {
return nil
}
svp := &aggregatedMessages{}
for _, m := range st.statuses {
svp.Messages = append(svp.Messages, m.Message)
}
// TODO: set aggregated signature
return svp
}