/
actor_v2_rule_validator.go
357 lines (301 loc) · 15.2 KB
/
actor_v2_rule_validator.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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package blsbft
import (
"github.com/incognitochain/incognito-chain/blockchain/types"
"github.com/incognitochain/incognito-chain/common"
"sort"
)
type IConsensusValidator interface {
FilterValidProposeBlockInfo(bestViewHash common.Hash, bestViewHeight uint64, finalViewHeight uint64, currentTimeSlot int64, proposeBlockInfos map[string]*ProposeBlockInfo) ([]*ProposeBlockInfo, []*ProposeBlockInfo, []string)
ValidateBlock(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) (bool, error)
ValidateConsensusRules(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) bool
}
type ConsensusValidatorLemma2 struct {
logger common.Logger
chain Chain
}
func NewConsensusValidatorLemma2(logger common.Logger, chain Chain) *ConsensusValidatorLemma2 {
return &ConsensusValidatorLemma2{logger: logger, chain: chain}
}
// FilterValidProposeBlockInfo validate received proposed block and return valid proposed block
// Special case: in case block is already inserted, try to send vote (avoid slashing)
// 1. by pass nil block
// 2. just validate recently
// 3. not in current time slot
// 4. not connect to best view
func (c ConsensusValidatorLemma2) FilterValidProposeBlockInfo(bestViewProposeHash common.Hash, bestViewHeight uint64, finalViewHeight uint64, currentTimeSlot int64, proposeBlockInfos map[string]*ProposeBlockInfo) ([]*ProposeBlockInfo, []*ProposeBlockInfo, []string) {
//Check for valid block to vote
validProposeBlock := []*ProposeBlockInfo{}
tryReVoteInsertedBlock := []*ProposeBlockInfo{}
invalidProposeBlock := []string{}
//get all block that has height = bestview height + 1(rule 2 & rule 3) (
for h, proposeBlockInfo := range proposeBlockInfos {
if proposeBlockInfo.block == nil {
continue
}
previousBlockHash := proposeBlockInfo.block.GetPrevHash()
previousView := c.chain.GetMultiView().GetViewByHash(previousBlockHash)
if previousView == nil {
continue
}
//must link to bestview, we expect all node having same block data having same bestview
if previousBlockHash.String() != c.chain.GetBestViewHash() {
continue
}
//special case: if we insert block too quick, before voting
//=> vote for this block (within TS,but block is inserted into bestview)
//this special case by pass validate with consensus rules
if proposeBlockInfo.block.GetHeight() == bestViewHeight &&
proposeBlockInfo.block.ProposeHash().IsEqual(&bestViewProposeHash) &&
!proposeBlockInfo.IsVoted {
tryReVoteInsertedBlock = append(tryReVoteInsertedBlock, proposeBlockInfo)
continue
}
// check if propose block in within TS
if previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()) != currentTimeSlot {
continue
}
//if the block height is not next height or current height
if proposeBlockInfo.block.GetHeight() != bestViewHeight+1 {
continue
}
// check if producer time > proposer time
if previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProduceTime()) > currentTimeSlot {
continue
}
//finality check
if proposeBlockInfo.block.GetFinalityHeight() != 0 {
if !proposeBlockInfo.IsValidLemma2Proof {
c.logger.Errorf("Reject block %+v as invalid lemma2 block, but finality height is set", proposeBlockInfo.block.FullHashString())
continue
}
if previousView.GetBlock().GetVersion() >= types.INSTANT_FINALITY_VERSION {
previousProposeTimeSlot := previousView.CalculateTimeSlot(previousView.GetBlock().GetProposeTime())
previousProduceTimeSlot := previousView.CalculateTimeSlot(previousView.GetBlock().GetProduceTime())
if previousView.GetBlock().GetFinalityHeight() == 0 && previousProposeTimeSlot != previousProduceTimeSlot {
c.logger.Errorf("Reject block %+v as previous block finality height not set (%+v) or produce/propose not the same (%+v)", proposeBlockInfo.block.FullHashString(), previousView.GetBlock().GetFinalityHeight(), previousProposeTimeSlot, previousProduceTimeSlot)
continue
}
}
}
if proposeBlockInfo.block.GetFinalityHeight() == 0 {
if previousView.GetBlock().GetVersion() >= types.INSTANT_FINALITY_VERSION {
previousProposeTimeSlot := previousView.CalculateTimeSlot(previousView.GetBlock().GetProposeTime())
previousProduceTimeSlot := previousView.CalculateTimeSlot(previousView.GetBlock().GetProduceTime())
if proposeBlockInfo.IsValidLemma2Proof && (previousView.GetBlock().GetFinalityHeight() != 0 || previousProposeTimeSlot == previousProduceTimeSlot) {
c.logger.Errorf("Reject block %+v as this block should set finality height", proposeBlockInfo.block.FullHashString())
continue
}
}
}
if proposeBlockInfo.block.GetHeight() < finalViewHeight {
invalidProposeBlock = append(invalidProposeBlock, h)
continue
}
validProposeBlock = append(validProposeBlock, proposeBlockInfo)
}
//rule 1: get history of vote for this height, vote if (round is lower than the vote before)
// or (round is equal but new proposer) or (there is no vote for this height yet)
sort.Slice(validProposeBlock, func(i, j int) bool {
return validProposeBlock[i].block.GetProduceTime() < validProposeBlock[j].block.GetProduceTime()
})
return validProposeBlock, tryReVoteInsertedBlock, invalidProposeBlock
}
func (c ConsensusValidatorLemma2) ValidateBlock(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) (bool, error) {
isValid := c.ValidateConsensusRules(lastVotedBlock, isVoteNextHeight, proposeBlockInfo)
if !isValid {
return isValid, nil
}
if proposeBlockInfo.IsVoted {
return true, nil
}
if !proposeBlockInfo.IsValid {
c.logger.Infof("validate block: %+v \n", proposeBlockInfo.block.FullHashString())
if err := c.chain.ValidatePreSignBlock(proposeBlockInfo.block, proposeBlockInfo.SigningCommittees, proposeBlockInfo.Committees); err != nil {
c.logger.Error(err)
return false, err
}
}
return true, nil
}
func (c ConsensusValidatorLemma2) ValidateConsensusRules(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) bool {
if !isVoteNextHeight {
c.logger.Infof("Block %+v is valid with because no block in height is voted yet %+v",
proposeBlockInfo.block.FullHashString(),
proposeBlockInfo.block.GetHeight())
return true
}
previousView := c.chain.GetViewByHash(proposeBlockInfo.block.GetPrevHash())
blockProduceTimeSlot := previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProduceTime())
lastBlockProduceTimeSlot := previousView.CalculateTimeSlot(lastVotedBlock.GetProduceTime())
if blockProduceTimeSlot < lastBlockProduceTimeSlot {
// blockProduceTimeSlot is smaller than voted block => vote for this block
c.logger.Infof("Block %+v is valid with rule 1, Block Produce Time %+v, < Last Block Produce Time %+v",
proposeBlockInfo.block.FullHashString(), blockProduceTimeSlot, lastBlockProduceTimeSlot)
return true
} else if blockProduceTimeSlot == lastBlockProduceTimeSlot &&
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()) > previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()) {
c.logger.Infof("Block %+v is valid with rule 2, Block Propose Time %+v, < Last Block Propose Time %+v",
proposeBlockInfo.block.FullHashString(),
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()),
previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()))
// block is old block (same round), but new proposer(larger timeslot) => vote again
return true
} else if proposeBlockInfo.block.CommitteeFromBlock().String() != lastVotedBlock.CommitteeFromBlock().String() {
c.logger.Infof("Block %+v is valid with rule 3, Block Produce Time %+v, < Last Block Produce Time %+v",
proposeBlockInfo.block.FullHashString(),
blockProduceTimeSlot, lastBlockProduceTimeSlot)
// blockProduceTimeSlot is larger or equal than voted block
return true
} // if not swap committees => do nothing
c.logger.Infof("ValidateConsensusRules failed, hash %+v, height %+v | "+
"blockProduceTs %+v, lastBlockProduceTs %+v |"+
"blockProposeTs %+v, lastBlockProposeTs %+v | "+
"isSameCommittee %+v",
proposeBlockInfo.block.FullHashString(),
proposeBlockInfo.block.GetHeight(),
blockProduceTimeSlot,
lastBlockProduceTimeSlot,
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()),
previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()),
proposeBlockInfo.block.CommitteeFromBlock().String() == lastVotedBlock.CommitteeFromBlock().String(),
)
return false
}
type ConsensusValidatorLemma1 struct {
logger common.Logger
chain Chain
}
func NewConsensusValidatorLemma1(logger common.Logger, chain Chain) *ConsensusValidatorLemma1 {
return &ConsensusValidatorLemma1{logger: logger, chain: chain}
}
func (c ConsensusValidatorLemma1) FilterValidProposeBlockInfo(bestViewHash common.Hash, bestViewHeight uint64, finalViewHeight uint64, currentTimeSlot int64, proposeBlockInfos map[string]*ProposeBlockInfo) ([]*ProposeBlockInfo, []*ProposeBlockInfo, []string) {
//Check for valid block to vote
validProposeBlock := []*ProposeBlockInfo{}
tryReVoteInsertedBlock := []*ProposeBlockInfo{}
invalidProposeBlock := []string{}
//get all block that has height = bestview height + 1(rule 2 & rule 3) (
for h, proposeBlockInfo := range proposeBlockInfos {
if proposeBlockInfo.block == nil {
continue
}
previousView := c.chain.GetViewByHash(proposeBlockInfo.block.GetPrevHash())
if previousView == nil {
continue
}
//// check if this time slot has been voted
//if a.votedTimeslot[c.chain.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime())] {
// continue
//}
//special case: if we insert block too quick, before voting
//=> vote for this block (within TS,but block is inserted into bestview)
//this special case by pass validate with consensus rules
if proposeBlockInfo.block.GetHeight() == bestViewHeight &&
proposeBlockInfo.block.Hash().IsEqual(&bestViewHash) &&
!proposeBlockInfo.IsVoted {
tryReVoteInsertedBlock = append(tryReVoteInsertedBlock, proposeBlockInfo)
continue
}
// check if propose block in within TS
if previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()) != currentTimeSlot {
continue
}
//if the block height is not next height or current height
if proposeBlockInfo.block.GetHeight() != bestViewHeight+1 {
continue
}
// check if producer time > proposer time
if previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProduceTime()) > currentTimeSlot {
continue
}
if proposeBlockInfo.block.GetHeight() < finalViewHeight {
invalidProposeBlock = append(invalidProposeBlock, h)
continue
}
validProposeBlock = append(validProposeBlock, proposeBlockInfo)
}
//rule 1: get history of vote for this height, vote if (round is lower than the vote before)
// or (round is equal but new proposer) or (there is no vote for this height yet)
sort.Slice(validProposeBlock, func(i, j int) bool {
return validProposeBlock[i].block.GetProduceTime() < validProposeBlock[j].block.GetProduceTime()
})
return validProposeBlock, tryReVoteInsertedBlock, invalidProposeBlock
}
func (c ConsensusValidatorLemma1) ValidateBlock(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) (bool, error) {
isValid := c.ValidateConsensusRules(lastVotedBlock, isVoteNextHeight, proposeBlockInfo)
if !isValid {
return isValid, nil
}
if proposeBlockInfo.IsVoted {
return true, nil
}
if !proposeBlockInfo.IsValid {
c.logger.Infof("validate block: %+v \n", proposeBlockInfo.block.FullHashString())
if err := c.chain.ValidatePreSignBlock(proposeBlockInfo.block, proposeBlockInfo.SigningCommittees, proposeBlockInfo.Committees); err != nil {
c.logger.Error(err)
return false, err
}
}
return true, nil
}
func (c ConsensusValidatorLemma1) ValidateConsensusRules(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) bool {
if !isVoteNextHeight {
c.logger.Infof("Block %+v is valid with because no block in height is voted yet %+v",
proposeBlockInfo.block.FullHashString(),
proposeBlockInfo.block.GetHeight())
return true
}
previousView := c.chain.GetViewByHash(proposeBlockInfo.block.GetPrevHash())
blockProduceTimeSlot := previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProduceTime())
lastBlockProduceTimeSlot := previousView.CalculateTimeSlot(lastVotedBlock.GetProduceTime())
if blockProduceTimeSlot < previousView.CalculateTimeSlot(lastVotedBlock.GetProduceTime()) {
c.logger.Infof("Block %+v is valid with rule 1, Block Produce Time %+v, < Last Block Produce Time %+v",
proposeBlockInfo.block.FullHashString(), blockProduceTimeSlot, lastBlockProduceTimeSlot)
// blockProduceTimeSlot is smaller than voted block => vote for this block
return true
} else if blockProduceTimeSlot == previousView.CalculateTimeSlot(lastVotedBlock.GetProduceTime()) &&
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()) > previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()) {
c.logger.Infof("Block %+v is valid with rule 2, Block Propose Time %+v, < Last Block Propose Time %+v",
proposeBlockInfo.block.FullHashString(),
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()),
previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()))
// block is old block (same round), but new proposer(larger timeslot) => vote again
return true
} else if proposeBlockInfo.block.CommitteeFromBlock().String() != lastVotedBlock.CommitteeFromBlock().String() {
// blockProduceTimeSlot is larger or equal than voted block
c.logger.Infof("Block %+v is valid with rule 3, Block Produce Time %+v, < Last Block Produce Time %+v",
proposeBlockInfo.block.FullHashString(),
blockProduceTimeSlot, lastBlockProduceTimeSlot)
return true
} // if not swap committees => do nothing
c.logger.Infof("ValidateConsensusRules failed, hash %+v, height %+v | "+
"blockProduceTs %+v, lastBlockProduceTs %+v |"+
"blockProposeTs %+v, lastBlockProposeTs %+v | "+
"isSameCommittee %+v",
proposeBlockInfo.block.FullHashString(),
proposeBlockInfo.block.GetHeight(),
blockProduceTimeSlot,
lastBlockProduceTimeSlot,
previousView.CalculateTimeSlot(proposeBlockInfo.block.GetProposeTime()),
previousView.CalculateTimeSlot(lastVotedBlock.GetProposeTime()),
proposeBlockInfo.block.CommitteeFromBlock().String() == lastVotedBlock.CommitteeFromBlock().String(),
)
return false
}
type ConsensusValidatorNoValidate struct {
}
func NewConsensusValidatorNoValidate() *ConsensusValidatorNoValidate {
return &ConsensusValidatorNoValidate{}
}
func (c ConsensusValidatorNoValidate) FilterValidProposeBlockInfo(bestViewHash common.Hash, bestViewHeight uint64, finalViewHeight uint64, currentTimeSlot int64, proposeBlockInfos map[string]*ProposeBlockInfo) ([]*ProposeBlockInfo, []*ProposeBlockInfo, []string) {
validProposeBlockInfo := []*ProposeBlockInfo{}
for _, v := range proposeBlockInfos {
validProposeBlockInfo = append(validProposeBlockInfo, v)
}
return validProposeBlockInfo, []*ProposeBlockInfo{}, []string{}
}
func (c ConsensusValidatorNoValidate) ValidateBlock(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) (bool, error) {
return true, nil
}
func (c ConsensusValidatorNoValidate) ValidateConsensusRules(lastVotedBlock types.BlockInterface, isVoteNextHeight bool, proposeBlockInfo *ProposeBlockInfo) bool {
return true
}