github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/qbft/core/justification.go (about) 1 package core 2 3 import ( 4 "errors" 5 "math/big" 6 7 "github.com/kisexp/xdchain/common" 8 "github.com/kisexp/xdchain/consensus/istanbul" 9 qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types" 10 "github.com/kisexp/xdchain/log" 11 ) 12 13 // Returns true if the `proposal` is justified by the set `roundChangeMessages` of ROUND-CHANGE messages 14 // and by the set `prepareMessages` of PREPARE messages. 15 // For this we must either have: 16 // - a quorum of ROUND-CHANGE messages with preparedRound and preparedBlockDigest equal to nil; or 17 // - a ROUND-CHANGE message (1) whose preparedRound is not nil and is equal or higher than the 18 // preparedRound of `quorumSize` ROUND-CHANGE messages and (2) whose preparedRound and 19 // preparedBlockDigest match the round and block of `quorumSize` PREPARE messages. 20 func isJustified( 21 proposal istanbul.Proposal, 22 roundChangeMessages []*qbfttypes.SignedRoundChangePayload, 23 prepareMessages []*qbfttypes.Prepare, 24 quorumSize int) error { 25 26 // Check the size of the set of ROUND-CHANGE messages 27 if len(roundChangeMessages) < quorumSize { 28 return errors.New("number of roundchange messages is less than required quorum of messages") 29 } 30 31 // Check the size of the set of PREPARE messages 32 if len(prepareMessages) != 0 && len(prepareMessages) < quorumSize { 33 return errors.New("number of prepared messages is less than required quorum of messages") 34 } 35 36 // If there are PREPARE messages, they all need to have the same round and match `proposal` 37 var preparedRound *big.Int 38 if len(prepareMessages) > 0 { 39 preparedRound = prepareMessages[0].Round 40 for _, spp := range prepareMessages { 41 if preparedRound.Cmp(spp.Round) != 0 || proposal.Hash() != spp.Digest { 42 return errors.New("prepared messages do not have same round or do not match proposal") 43 } 44 } 45 } 46 47 if preparedRound == nil { 48 return hasQuorumOfRoundChangeMessagesForNil(roundChangeMessages, quorumSize) 49 } else { 50 return hasQuorumOfRoundChangeMessagesForPreparedRoundAndBlock(roundChangeMessages, preparedRound, proposal, quorumSize) 51 } 52 } 53 54 // Checks whether a set of ROUND-CHANGE messages has `quorumSize` messages with nil prepared round and 55 // prepared block. 56 func hasQuorumOfRoundChangeMessagesForNil(roundChangeMessages []*qbfttypes.SignedRoundChangePayload, quorumSize int) error { 57 nilCount := 0 58 for _, m := range roundChangeMessages { 59 log.Trace("QBFT: hasQuorumOfRoundChangeMessagesForNil", "rc", m) 60 if (m.PreparedRound == nil || m.PreparedRound.Cmp(common.Big0) == 0) && common.EmptyHash(m.PreparedDigest) { 61 nilCount++ 62 if nilCount == quorumSize { 63 return nil 64 } 65 } 66 } 67 return errors.New("quorum of roundchange messages with nil prepared round not found") 68 } 69 70 // Checks whether a set of ROUND-CHANGE messages has some message with `preparedRound` and `preparedBlockDigest`, 71 // and has `quorumSize` messages with prepared round equal to nil or equal or lower than `preparedRound`. 72 func hasQuorumOfRoundChangeMessagesForPreparedRoundAndBlock(roundChangeMessages []*qbfttypes.SignedRoundChangePayload, preparedRound *big.Int, preparedBlock istanbul.Proposal, quorumSize int) error { 73 lowerOrEqualRoundCount := 0 74 hasMatchingMessage := false 75 for _, m := range roundChangeMessages { 76 log.Trace("QBFT: hasQuorumOfRoundChangeMessagesForPreparedRoundAndBlock", "rc", m) 77 if m.PreparedRound == nil || m.PreparedRound.Cmp(preparedRound) <= 0 { 78 lowerOrEqualRoundCount++ 79 if m.PreparedRound != nil && m.PreparedRound.Cmp(preparedRound) == 0 && m.PreparedDigest == preparedBlock.Hash() { 80 hasMatchingMessage = true 81 } 82 if lowerOrEqualRoundCount >= quorumSize && hasMatchingMessage { 83 return nil 84 } 85 } 86 } 87 88 return errors.New("quorum of roundchange messages with prepared round and proposal not found") 89 } 90 91 // Checks whether the round and block of a set of PREPARE messages of at least quorumSize match the 92 // preparedRound and preparedBlockDigest of a ROUND-CHANGE qbfttypes. 93 func hasMatchingRoundChangeAndPrepares( 94 roundChange *qbfttypes.RoundChange, prepareMessages []*qbfttypes.Prepare, quorumSize int) error { 95 96 if len(prepareMessages) < quorumSize { 97 return errors.New("number of prepare messages is less than quorum of messages") 98 } 99 100 for _, spp := range prepareMessages { 101 if spp.Digest != roundChange.PreparedDigest { 102 return errors.New("prepared message digest does not match roundchange prepared digest") 103 } 104 if spp.Round.Cmp(roundChange.PreparedRound) != 0 { 105 return errors.New("round number in prepared message does not match prepared round in roundchange") 106 } 107 } 108 return nil 109 }