Skip to content

Commit 212798e

Browse files
authored
Merge pull request #6717 from multiversx/equivalent-proofs-stabilization-2
Equivalent proofs stabilization 2
2 parents dc29ec4 + a0a6bb5 commit 212798e

33 files changed

+615
-175
lines changed

cmd/node/config/config.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242

4343
# ChainParametersByEpoch defines chain operation configurable values that can be modified based on epochs
4444
ChainParametersByEpoch = [
45-
{ EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false }
45+
{ EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false },
46+
{ EnableEpoch = 8, RoundDuration = 6000, ShardConsensusGroupSize = 10, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false }
4647
]
4748

4849
[HardwareRequirements]

common/common.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ func IsEpochChangeBlockForFlagActivation(header data.HeaderHandler, enableEpochs
4141
return isStartOfEpochBlock && isBlockInActivationEpoch
4242
}
4343

44-
// IsFlagEnabledAfterEpochsStartBlock returns true if the flag is enabled for the header, but it is not the epoch start block
45-
func IsFlagEnabledAfterEpochsStartBlock(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool {
44+
// isFlagEnabledAfterEpochsStartBlock returns true if the flag is enabled for the header, but it is not the epoch start block
45+
func isFlagEnabledAfterEpochsStartBlock(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool {
4646
isFlagEnabled := enableEpochsHandler.IsFlagEnabledInEpoch(flag, header.GetEpoch())
4747
isEpochStartBlock := IsEpochChangeBlockForFlagActivation(header, enableEpochsHandler, flag)
4848
return isFlagEnabled && !isEpochStartBlock
4949
}
5050

5151
// ShouldBlockHavePrevProof returns true if the block should have a proof
5252
func ShouldBlockHavePrevProof(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool {
53-
return IsFlagEnabledAfterEpochsStartBlock(header, enableEpochsHandler, flag) && header.GetNonce() > 1
53+
return isFlagEnabledAfterEpochsStartBlock(header, enableEpochsHandler, flag) && header.GetNonce() > 1
5454
}
5555

5656
// VerifyProofAgainstHeader verifies the fields on the proof match the ones on the header
@@ -71,6 +71,9 @@ func VerifyProofAgainstHeader(proof data.HeaderProofHandler, header data.HeaderH
7171
if proof.GetHeaderRound() != header.GetRound() {
7272
return fmt.Errorf("%w, round mismatch", ErrInvalidHeaderProof)
7373
}
74+
if proof.GetIsStartOfEpoch() != header.IsStartOfEpochBlock() {
75+
return fmt.Errorf("%w, is start of epoch mismatch", ErrInvalidHeaderProof)
76+
}
7477

7578
return nil
7679
}

consensus/chronology/chronology.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func (chr *chronology) RemoveAllSubrounds() {
104104

105105
chr.subrounds = make(map[int]int)
106106
chr.subroundHandlers = make([]consensus.SubroundHandler, 0)
107+
chr.subroundId = srBeforeStartRound
107108

108109
chr.mutSubrounds.Unlock()
109110
}

consensus/spos/bls/v2/export_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func (sr *subroundEndRound) DoEndRoundJobByNode() bool {
263263

264264
// CreateAndBroadcastProof calls the unexported createAndBroadcastHeaderFinalInfo function
265265
func (sr *subroundEndRound) CreateAndBroadcastProof(signature []byte, bitmap []byte) {
266-
_, _ = sr.createAndBroadcastProof(signature, bitmap)
266+
_ = sr.createAndBroadcastProof(signature, bitmap)
267267
}
268268

269269
// ReceivedProof calls the unexported receivedProof function

consensus/spos/bls/v2/subroundBlock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ func (sr *subroundBlock) saveProofForPreviousHeaderIfNeeded(header data.HeaderHa
364364
proof := header.GetPreviousProof()
365365
err := common.VerifyProofAgainstHeader(proof, prevHeader)
366366
if err != nil {
367-
log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof, %w", err)
367+
log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof", "error", err.Error())
368368
return
369369
}
370370

consensus/spos/bls/v2/subroundEndRound.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/multiversx/mx-chain-core-go/core"
1313
"github.com/multiversx/mx-chain-core-go/core/check"
14-
"github.com/multiversx/mx-chain-core-go/data"
1514
"github.com/multiversx/mx-chain-core-go/data/block"
1615
"github.com/multiversx/mx-chain-core-go/display"
1716

@@ -247,10 +246,36 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool {
247246
if !sr.waitForSignalSync() {
248247
return false
249248
}
249+
sr.sendProof()
250250
}
251251

252-
proof, ok := sr.sendProof()
253-
if !ok {
252+
return sr.finalizeConfirmedBlock()
253+
}
254+
255+
func (sr *subroundEndRound) waitForProof() bool {
256+
shardID := sr.ShardCoordinator().SelfId()
257+
headerHash := sr.GetData()
258+
if sr.EquivalentProofsPool().HasProof(shardID, headerHash) {
259+
return true
260+
}
261+
262+
ctx, cancel := context.WithTimeout(context.Background(), sr.RoundHandler().TimeDuration())
263+
defer cancel()
264+
265+
for {
266+
select {
267+
case <-time.After(time.Millisecond):
268+
if sr.EquivalentProofsPool().HasProof(shardID, headerHash) {
269+
return true
270+
}
271+
case <-ctx.Done():
272+
return false
273+
}
274+
}
275+
}
276+
277+
func (sr *subroundEndRound) finalizeConfirmedBlock() bool {
278+
if !sr.waitForProof() {
254279
return false
255280
}
256281

@@ -259,14 +284,6 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool {
259284
return false
260285
}
261286

262-
// if proof not nil, it was created and broadcasted so it has to be added to the pool
263-
if proof != nil {
264-
ok := sr.EquivalentProofsPool().AddProof(proof)
265-
if !ok {
266-
log.Trace("doEndRoundJobByNode.AddProof", "added", ok)
267-
}
268-
}
269-
270287
sr.SetStatus(sr.Current(), spos.SsFinished)
271288

272289
sr.worker.DisplayStatistics()
@@ -281,42 +298,44 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool {
281298
return true
282299
}
283300

284-
func (sr *subroundEndRound) sendProof() (data.HeaderProofHandler, bool) {
301+
func (sr *subroundEndRound) sendProof() {
285302
if !sr.shouldSendProof() {
286-
return nil, true
303+
return
287304
}
288305

289306
bitmap := sr.GenerateBitmap(bls.SrSignature)
290307
err := sr.checkSignaturesValidity(bitmap)
291308
if err != nil {
292309
log.Debug("sendProof.checkSignaturesValidity", "error", err.Error())
293-
return nil, false
310+
return
294311
}
295312

296313
// Aggregate signatures, handle invalid signers and send final info if needed
297314
bitmap, sig, err := sr.aggregateSigsAndHandleInvalidSigners(bitmap)
298315
if err != nil {
299316
log.Debug("sendProof.aggregateSigsAndHandleInvalidSigners", "error", err.Error())
300-
return nil, false
317+
return
301318
}
302319

303320
ok := sr.ScheduledProcessor().IsProcessedOKWithTimeout()
304321
// placeholder for subroundEndRound.doEndRoundJobByLeader script
305322
if !ok {
306-
return nil, false
323+
return
307324
}
308325

309326
roundHandler := sr.RoundHandler()
310327
if roundHandler.RemainingTime(roundHandler.TimeStamp(), roundHandler.TimeDuration()) < 0 {
311328
log.Debug("sendProof: time is out -> cancel broadcasting final info and header",
312329
"round time stamp", roundHandler.TimeStamp(),
313330
"current time", time.Now())
314-
return nil, false
331+
return
315332
}
316333

317334
// broadcast header proof
318-
proof, err := sr.createAndBroadcastProof(sig, bitmap)
319-
return proof, err == nil
335+
err = sr.createAndBroadcastProof(sig, bitmap)
336+
if err != nil {
337+
log.Warn("sendProof.createAndBroadcastProof", "error", err.Error())
338+
}
320339
}
321340

322341
func (sr *subroundEndRound) shouldSendProof() bool {
@@ -524,7 +543,7 @@ func (sr *subroundEndRound) computeAggSigOnValidNodes() ([]byte, []byte, error)
524543
return bitmap, sig, nil
525544
}
526545

527-
func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) (*block.HeaderProof, error) {
546+
func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) error {
528547
headerProof := &block.HeaderProof{
529548
PubKeysBitmap: bitmap,
530549
AggregatedSignature: signature,
@@ -533,18 +552,19 @@ func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []b
533552
HeaderNonce: sr.GetHeader().GetNonce(),
534553
HeaderShardId: sr.GetHeader().GetShardID(),
535554
HeaderRound: sr.GetHeader().GetRound(),
555+
IsStartOfEpoch: sr.GetHeader().IsStartOfEpochBlock(),
536556
}
537557

538558
err := sr.BroadcastMessenger().BroadcastEquivalentProof(headerProof, []byte(sr.SelfPubKey()))
539559
if err != nil {
540-
return nil, err
560+
return err
541561
}
542562

543563
log.Debug("step 3: block header proof has been sent",
544564
"PubKeysBitmap", bitmap,
545565
"AggregateSignature", signature)
546566

547-
return headerProof, nil
567+
return nil
548568
}
549569

550570
func (sr *subroundEndRound) createAndBroadcastInvalidSigners(invalidSigners []byte) {

consensus/spos/bls/v2/subroundEndRound_test.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,11 @@ func TestSubroundEndRound_DoEndRoundJobAllOK(t *testing.T) {
572572
t.Parallel()
573573

574574
container := consensusMocks.InitConsensusCore()
575+
container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{
576+
HasProofCalled: func(shardID uint32, headerHash []byte) bool {
577+
return true
578+
},
579+
})
575580
sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{})
576581
sr.SetSelfPubKey("A")
577582

@@ -1176,6 +1181,16 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) {
11761181
t.Parallel()
11771182

11781183
container := consensusMocks.InitConsensusCore()
1184+
numCalls := 0
1185+
container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{
1186+
HasProofCalled: func(shardID uint32, headerHash []byte) bool {
1187+
if numCalls <= 1 {
1188+
numCalls++
1189+
return false
1190+
}
1191+
return true
1192+
},
1193+
})
11791194
sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{})
11801195

11811196
verifySigShareNumCalls := 0
@@ -1227,31 +1242,24 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) {
12271242
t.Run("should work with equivalent messages flag active", func(t *testing.T) {
12281243
t.Parallel()
12291244

1230-
providedPrevSig := []byte("prev sig")
1231-
providedPrevBitmap := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1}
12321245
container := consensusMocks.InitConsensusCore()
12331246
container.SetBlockchain(&testscommon.ChainHandlerStub{
12341247
GetGenesisHeaderCalled: func() data.HeaderHandler {
12351248
return &block.HeaderV2{}
12361249
},
12371250
})
1251+
container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{
1252+
HasProofCalled: func(shardID uint32, headerHash []byte) bool {
1253+
return true
1254+
},
1255+
})
12381256
enableEpochsHandler := &enableEpochsHandlerMock.EnableEpochsHandlerStub{
12391257
IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool {
12401258
return flag == common.EquivalentMessagesFlag
12411259
},
12421260
}
12431261
container.SetEnableEpochsHandler(enableEpochsHandler)
12441262

1245-
wasSetCurrentHeaderProofCalled := false
1246-
container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{
1247-
AddProofCalled: func(headerProof data.HeaderProofHandler) bool {
1248-
wasSetCurrentHeaderProofCalled = true
1249-
require.NotEqual(t, providedPrevSig, headerProof.GetAggregatedSignature())
1250-
require.NotEqual(t, providedPrevBitmap, headerProof.GetPubKeysBitmap())
1251-
return true
1252-
},
1253-
})
1254-
12551263
ch := make(chan bool, 1)
12561264
consensusState := initializers.InitConsensusState()
12571265
sr, _ := spos.NewSubround(
@@ -1295,7 +1303,6 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) {
12951303

12961304
r := srEndRound.DoEndRoundJobByNode()
12971305
require.True(t, r)
1298-
require.True(t, wasSetCurrentHeaderProofCalled)
12991306
})
13001307
}
13011308

dataRetriever/dataPool/proofsCache/proofsPool.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,22 @@ func (pp *proofsPool) AddProof(
5353
}
5454

5555
pp.mutCache.Lock()
56-
defer pp.mutCache.Unlock()
57-
5856
proofsPerShard, ok := pp.cache[shardID]
5957
if !ok {
6058
proofsPerShard = newProofsCache()
6159
pp.cache[shardID] = proofsPerShard
6260
}
61+
pp.mutCache.Unlock()
6362

64-
log.Trace("added proof to pool",
63+
log.Debug("added proof to pool",
6564
"header hash", headerProof.GetHeaderHash(),
6665
"epoch", headerProof.GetHeaderEpoch(),
6766
"nonce", headerProof.GetHeaderNonce(),
6867
"shardID", headerProof.GetHeaderShardId(),
6968
"pubKeys bitmap", headerProof.GetPubKeysBitmap(),
69+
"round", headerProof.GetHeaderRound(),
70+
"nonce", headerProof.GetHeaderNonce(),
71+
"isStartOfEpoch", headerProof.GetIsStartOfEpoch(),
7072
)
7173

7274
proofsPerShard.addProof(headerProof)
@@ -98,9 +100,8 @@ func (pp *proofsPool) CleanupProofsBehindNonce(shardID uint32, nonce uint64) err
98100
nonce -= pp.cleanupNonceDelta
99101

100102
pp.mutCache.RLock()
101-
defer pp.mutCache.RUnlock()
102-
103103
proofsPerShard, ok := pp.cache[shardID]
104+
pp.mutCache.RUnlock()
104105
if !ok {
105106
return fmt.Errorf("%w: proofs cache per shard not found, shard ID: %d", ErrMissingProof, shardID)
106107
}
@@ -123,16 +124,14 @@ func (pp *proofsPool) GetProof(
123124
if headerHash == nil {
124125
return nil, fmt.Errorf("nil header hash")
125126
}
126-
127-
pp.mutCache.RLock()
128-
defer pp.mutCache.RUnlock()
129-
130127
log.Trace("trying to get proof",
131128
"headerHash", headerHash,
132129
"shardID", shardID,
133130
)
134131

132+
pp.mutCache.RLock()
135133
proofsPerShard, ok := pp.cache[shardID]
134+
pp.mutCache.RUnlock()
136135
if !ok {
137136
return nil, fmt.Errorf("%w: proofs cache per shard not found, shard ID: %d", ErrMissingProof, shardID)
138137
}

epochStart/bootstrap/epochStartMetaBlockProcessor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context,
264264
return epochStart.ErrNilMetaBlock
265265
}
266266

267-
err := e.requestConfirmationMetaBlock(metaBlock.GetNonce())
267+
err := e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1)
268268
if err != nil {
269269
return err
270270
}
@@ -278,7 +278,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context,
278278
case <-ctx.Done():
279279
return epochStart.ErrTimeoutWaitingForMetaBlock
280280
case <-chanRequests:
281-
err = e.requestConfirmationMetaBlock(metaBlock.GetNonce())
281+
err = e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1)
282282
if err != nil {
283283
return err
284284
}

epochStart/shardchain/trigger.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ func (t *trigger) receivedProof(headerProof data.HeaderProofHandler) {
569569
if headerProof.GetHeaderShardId() != core.MetachainShardId {
570570
return
571571
}
572+
572573
t.mutTrigger.Lock()
573574
defer t.mutTrigger.Unlock()
574575

0 commit comments

Comments
 (0)