justification.go 4.5 KB

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