@@ -550,6 +550,7 @@ func (e *Epoch) loadFinalizationRecord(r []byte) error {
550550 if err != nil {
551551 return err
552552 }
553+ e .pruneCompetingRounds (finalization )
553554 e .Logger .Info ("Finalization Recovered From WAL" , zap .Uint64 ("Round" , finalization .Finalization .Round ))
554555 return nil
555556}
@@ -1348,7 +1349,7 @@ func (e *Epoch) persistFinalization(finalization common.Finalization) error {
13481349 e .blockVerificationScheduler .ExecuteBlockDependents (finalization .Finalization .Digest )
13491350 e .blockVerificationScheduler .RemoveOldTasks (finalization .Finalization .Seq )
13501351 e .replicationState .clearBlockDependencyTasks (finalization .Finalization .Digest , finalization .Finalization .Seq , true )
1351- e .deleteOldEmptyVotes (finalization . Finalization . Round )
1352+ e .pruneCompetingRounds (finalization )
13521353
13531354 if finalization .Finalization .Seq == nextSeqToCommit {
13541355 if err := e .indexFinalizations (finalization .Finalization .Round ); err != nil {
@@ -1778,7 +1779,7 @@ func (e *Epoch) handleEmptyNotarizationMessage(emptyNotarization *common.EmptyNo
17781779
17791780 e .Logger .Verbo ("Received empty notarization message" , zap .Uint64 ("round" , vote .Round ))
17801781
1781- if e .isVoteForFinalizedRound (vote .Round ) {
1782+ if e .isBelowHighestFinalizedRound (vote .Round ) {
17821783 e .Logger .Debug ("Received an empty notarization for a too low round" ,
17831784 zap .Uint64 ("round" , vote .Round ), zap .Uint64 ("our round" , e .round ))
17841785
@@ -1814,7 +1815,7 @@ func (e *Epoch) handleEmptyNotarizationMessage(emptyNotarization *common.EmptyNo
18141815}
18151816
18161817// we do not care to process votes for rounds where we have finalized
1817- func (e * Epoch ) isVoteForFinalizedRound (round uint64 ) bool {
1818+ func (e * Epoch ) isBelowHighestFinalizedRound (round uint64 ) bool {
18181819 max := uint64 (0 )
18191820
18201821 for _ , round := range e .rounds {
@@ -1840,7 +1841,7 @@ func (e *Epoch) handleNotarizationMessage(message *common.Notarization, from com
18401841 e .Logger .Verbo ("Received notarization message" ,
18411842 zap .Stringer ("from" , from ), zap .Uint64 ("round" , vote .Round ))
18421843
1843- if e .isVoteForFinalizedRound (vote .Round ) {
1844+ if e .isBelowHighestFinalizedRound (vote .Round ) {
18441845 return nil
18451846 }
18461847
@@ -2230,6 +2231,12 @@ func (e *Epoch) createBlockVerificationTask(block common.Block, from common.Node
22302231 return common.Digest {}
22312232 }
22322233
2234+ // A higher round was finalized while we were verifying, so this block can no longer be finalized.
2235+ if e .isBelowHighestFinalizedRound (md .Round ) {
2236+ e .Logger .Debug ("Dropping proposal for a round below a finalized round" , zap .Uint64 ("round" , md .Round ))
2237+ return common.Digest {}
2238+ }
2239+
22332240 if ! e .storeProposal (verifiedBlock ) {
22342241 e .Logger .Debug ("Unable to store proposed block for the round" , zap .Stringer ("NodeID" , from ), zap .Uint64 ("round" , md .Round ))
22352242 return common.Digest {}
@@ -2383,6 +2390,12 @@ func (e *Epoch) createNotarizedBlockVerificationTask(block common.Block, notariz
23832390 e .lock .Lock ()
23842391 defer e .lock .Unlock ()
23852392
2393+ // A higher round was finalized while we were verifying, so this block can no longer be finalized.
2394+ if e .isBelowHighestFinalizedRound (md .Round ) {
2395+ e .Logger .Debug ("Dropping notarized block for a round below a finalized round" , zap .Uint64 ("round" , md .Round ))
2396+ return common.Digest {}
2397+ }
2398+
23862399 // we started verifying the block when we didn't have a notarization, however its
23872400 // possible we received a notarization or empty notarization for this block in the meantime.
23882401 round , ok := e .rounds [md .Round ]
@@ -3078,9 +3091,30 @@ func (e *Epoch) deleteRounds(round uint64) {
30783091 }
30793092}
30803093
3081- func (e * Epoch ) deleteOldEmptyVotes (finalizedRound uint64 ) {
3094+ // pruneCompetingRounds deletes the blocks and empty votes below the finalized round that are not
3095+ // part of the finalized block's chain. Ex. if we have blockchains b1 <- b3 <- b5, and b1 <- b2 <- b4,
3096+ // a call to pruneCompetingRounds(finalization b5), will prune blocks b2 & b4 from the rounds map.
3097+ func (e * Epoch ) pruneCompetingRounds (finalization common.Finalization ) {
3098+ prev := finalization .Finalization .Prev
3099+ minRound := e .minRoundInRoundsMap ()
3100+ for r := finalization .Finalization .Round ; r > minRound ; r -- {
3101+ round , exists := e .rounds [r - 1 ]
3102+ if ! exists {
3103+ continue
3104+ }
3105+
3106+ // don't delete this round, since the block is an ancestor of finalization in our chain
3107+ if header := round .block .BlockHeader (); header .Digest == prev {
3108+ prev = header .Prev
3109+ continue
3110+ }
3111+
3112+ e .Logger .Debug ("Pruning block not on the finalized chain" , zap .Uint64 ("round" , r - 1 ), zap .Uint64 ("finalized round" , finalization .Finalization .Round ))
3113+ delete (e .rounds , r - 1 )
3114+ }
3115+
30823116 for r := range e .emptyVotes {
3083- if r < finalizedRound {
3117+ if r < finalization . Finalization . Round {
30843118 delete (e .emptyVotes , r )
30853119 }
30863120 }
@@ -3602,7 +3636,7 @@ func (e *Epoch) processQuorumRound(round *common.QuorumRound, from common.NodeID
36023636 return nil
36033637 }
36043638
3605- if round .Finalization == nil && e .isVoteForFinalizedRound (round .GetRound ()) {
3639+ if round .Finalization == nil && e .isBelowHighestFinalizedRound (round .GetRound ()) {
36063640 return fmt .Errorf ("received a quorum round for a round that has been finalized. round: %d; seq: %d" , round .GetRound (), round .GetSequence ())
36073641 }
36083642
0 commit comments