-
Notifications
You must be signed in to change notification settings - Fork 199
/
subround.go
210 lines (181 loc) · 5.56 KB
/
subround.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package spos
import (
"context"
"time"
"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/core/check"
"github.com/ElrondNetwork/elrond-go/consensus"
)
var _ consensus.SubroundHandler = (*Subround)(nil)
// Subround struct contains the needed data for one Subround and the Subround properties. It defines a Subround
// with it's properties (it's ID, next Subround ID, it's duration, it's name) and also it has some handler functions
// which should be set. Job function will be the main function of this Subround, Extend function will handle the overtime
// situation of the Subround and Check function will decide if in this Subround the consensus is achieved
type Subround struct {
ConsensusCoreHandler
*ConsensusState
previous int
current int
next int
startTime int64
endTime int64
name string
chainID []byte
currentPid core.PeerID
consensusStateChangedChannel chan bool
executeStoredMessages func()
appStatusHandler core.AppStatusHandler
Job func(ctx context.Context) bool // method does the Subround Job and send the result to the peers
Check func() bool // method checks if the consensus of the Subround is done
Extend func(subroundId int) // method is called when round time is out
}
// NewSubround creates a new SubroundId object
func NewSubround(
previous int,
current int,
next int,
startTime int64,
endTime int64,
name string,
consensusState *ConsensusState,
consensusStateChangedChannel chan bool,
executeStoredMessages func(),
container ConsensusCoreHandler,
chainID []byte,
currentPid core.PeerID,
appStatusHandler core.AppStatusHandler,
) (*Subround, error) {
err := checkNewSubroundParams(
consensusState,
consensusStateChangedChannel,
executeStoredMessages,
container,
chainID,
appStatusHandler,
)
if err != nil {
return nil, err
}
sr := Subround{
ConsensusCoreHandler: container,
ConsensusState: consensusState,
previous: previous,
current: current,
next: next,
startTime: startTime,
endTime: endTime,
name: name,
chainID: chainID,
consensusStateChangedChannel: consensusStateChangedChannel,
executeStoredMessages: executeStoredMessages,
Job: nil,
Check: nil,
Extend: nil,
appStatusHandler: appStatusHandler,
currentPid: currentPid,
}
return &sr, nil
}
func checkNewSubroundParams(
state *ConsensusState,
consensusStateChangedChannel chan bool,
executeStoredMessages func(),
container ConsensusCoreHandler,
chainID []byte,
appStatusHandler core.AppStatusHandler,
) error {
err := ValidateConsensusCore(container)
if err != nil {
return err
}
if consensusStateChangedChannel == nil {
return ErrNilChannel
}
if state == nil {
return ErrNilConsensusState
}
if executeStoredMessages == nil {
return ErrNilExecuteStoredMessages
}
if len(chainID) == 0 {
return ErrInvalidChainID
}
if check.IfNil(appStatusHandler) {
return ErrNilAppStatusHandler
}
return nil
}
// DoWork method actually does the work of this Subround. First it tries to do the Job of the Subround then it will
// Check the consensus. If the upper time limit of this Subround is reached, the Extend method will be called before
// returning. If this method returns true the chronology will advance to the next Subround.
func (sr *Subround) DoWork(ctx context.Context, roundHandler consensus.RoundHandler) bool {
if sr.Job == nil || sr.Check == nil {
return false
}
// execute stored messages which were received in this new round but before this initialisation
go sr.executeStoredMessages()
startTime := roundHandler.TimeStamp()
maxTime := roundHandler.TimeDuration() * MaxThresholdPercent / 100
sr.Job(ctx)
if sr.Check() {
return true
}
for {
select {
case <-sr.consensusStateChangedChannel:
if sr.Check() {
return true
}
case <-time.After(roundHandler.RemainingTime(startTime, maxTime)):
if sr.Extend != nil {
sr.RoundCanceled = true
sr.Extend(sr.current)
}
return false
}
}
}
// Previous method returns the ID of the previous Subround
func (sr *Subround) Previous() int {
return sr.previous
}
// Current method returns the ID of the current Subround
func (sr *Subround) Current() int {
return sr.current
}
// Next method returns the ID of the next Subround
func (sr *Subround) Next() int {
return sr.next
}
// StartTime method returns the start time of the Subround
func (sr *Subround) StartTime() int64 {
return sr.startTime
}
// EndTime method returns the upper time limit of the Subround
func (sr *Subround) EndTime() int64 {
return sr.endTime
}
// Name method returns the name of the Subround
func (sr *Subround) Name() string {
return sr.name
}
// ChainID method returns the current chain ID
func (sr *Subround) ChainID() []byte {
return sr.chainID
}
// CurrentPid returns the current p2p peer ID
func (sr *Subround) CurrentPid() core.PeerID {
return sr.currentPid
}
// AppStatusHandler method returns the appStatusHandler instance
func (sr *Subround) AppStatusHandler() core.AppStatusHandler {
return sr.appStatusHandler
}
// ConsensusChannel method returns the consensus channel
func (sr *Subround) ConsensusChannel() chan bool {
return sr.consensusStateChangedChannel
}
// IsInterfaceNil returns true if there is no value under the interface
func (sr *Subround) IsInterfaceNil() bool {
return sr == nil
}