justification_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package core
  2. import (
  3. "fmt"
  4. "math/big"
  5. "math/rand"
  6. "testing"
  7. "time"
  8. "github.com/ethereum/go-ethereum/common"
  9. "github.com/ethereum/go-ethereum/consensus/istanbul"
  10. qbfttypes "github.com/ethereum/go-ethereum/consensus/istanbul/qbft/types"
  11. "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
  12. "github.com/ethereum/go-ethereum/core/types"
  13. "github.com/ethereum/go-ethereum/crypto"
  14. )
  15. // Tests combinations of justifications that evaluate to true.
  16. func TestJustifyTrue(t *testing.T) {
  17. for quorumSize := 3; quorumSize <= 10; quorumSize++ {
  18. // All ROUND-CHANGE messages have pr/pb nil
  19. testParameterizedCase(t, quorumSize, quorumSize, 0, 0, 0, 0, 0, true)
  20. // Some ROUND-CHANGE message has pr/pb not nil
  21. for equal := 1; equal <= quorumSize; equal++ {
  22. for less := 0; less <= quorumSize-equal; less++ {
  23. nil := quorumSize - equal - less
  24. testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize, 0, true)
  25. }
  26. }
  27. }
  28. }
  29. // Tests combinations of justifications that evaluate to false.
  30. func TestJustifyFalse(t *testing.T) {
  31. for quorumSize := 3; quorumSize <= 10; quorumSize++ {
  32. // Total ROUND-CHANGE messages less than quorumSize
  33. // all have pr/pb nil
  34. for totalRoundChange := 0; totalRoundChange < quorumSize; totalRoundChange++ {
  35. testParameterizedCase(t, quorumSize, totalRoundChange, 0, 0, 0, 0, 0, false)
  36. }
  37. // some has pr/pb not nil
  38. for totalRoundChange := 0; totalRoundChange < quorumSize; totalRoundChange++ {
  39. for equal := 1; equal <= totalRoundChange; equal++ {
  40. for less := 0; less <= totalRoundChange-equal; less++ {
  41. nil := totalRoundChange - equal - less
  42. testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize, 0, false)
  43. }
  44. }
  45. }
  46. // Total ROUND-CHANGE messages equal to quorumSize
  47. for equal := 1; equal <= quorumSize; equal++ {
  48. for less := 0; less <= quorumSize-equal; less++ {
  49. nil := quorumSize - equal - less
  50. // Total PREPARE messages less than quorumSize
  51. for total := 0; total < quorumSize; total++ {
  52. testParameterizedCase(t, quorumSize, nil, equal, less, 0, total, quorumSize-total, false)
  53. }
  54. // Total PREPARE messages equal to quorumSize and some PREPARE message has round different than others
  55. for different := 1; different <= quorumSize; different++ {
  56. testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize-different, different, false)
  57. }
  58. }
  59. }
  60. }
  61. }
  62. func testParameterizedCase(
  63. t *testing.T,
  64. quorumSize int,
  65. rcForNil int,
  66. rcEqualToTargetRound int,
  67. rcLowerThanTargetRound int,
  68. rcHigherThanTargetRound int,
  69. preparesForTargetRound int,
  70. preparesNotForTargetRound int,
  71. messageJustified bool) {
  72. pp := istanbul.NewRoundRobinProposerPolicy()
  73. pp.Use(istanbul.ValidatorSortByByte())
  74. validatorSet := validator.NewSet(generateValidators(quorumSize), pp)
  75. block := makeBlock(1)
  76. var round int64 = 10
  77. var targetPreparedRound int64 = 5
  78. rng := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
  79. if rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound+rcHigherThanTargetRound > quorumSize {
  80. t.Errorf("rcForNil (%v) + rcEqualToTargetRound (%v) + rcLowerThanTargetRound (%v) + rcHigherThanTargetRound (%v) > quorumSize (%v)",
  81. rcForNil, rcEqualToTargetRound, rcLowerThanTargetRound, rcHigherThanTargetRound, quorumSize)
  82. }
  83. if preparesForTargetRound+preparesNotForTargetRound > quorumSize {
  84. t.Errorf("preparesForTargetRound (%v) + preparesNotForTargetRound (%v) > quorumSize (%v)", preparesForTargetRound, preparesNotForTargetRound, quorumSize)
  85. }
  86. // ROUND-CHANGE messages
  87. roundChangeMessages := make([]*qbfttypes.SignedRoundChangePayload, 0)
  88. for index, validator := range validatorSet.List() {
  89. var m *qbfttypes.SignedRoundChangePayload
  90. if index < rcForNil {
  91. m = createRoundChangeMessage(validator.Address(), round, 0, nil)
  92. } else if index >= rcForNil && index < rcForNil+rcEqualToTargetRound {
  93. m = createRoundChangeMessage(validator.Address(), round, targetPreparedRound, block)
  94. } else if index >= rcForNil+rcEqualToTargetRound && index < rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound {
  95. m = createRoundChangeMessage(validator.Address(), round, int64(rng.Intn(int(targetPreparedRound)-1)+1), block)
  96. } else if index >= rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound && index < rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound+rcHigherThanTargetRound {
  97. m = createRoundChangeMessage(validator.Address(), round, int64(rng.Intn(int(targetPreparedRound))+int(targetPreparedRound)+1), block)
  98. } else {
  99. break
  100. }
  101. roundChangeMessages = append(roundChangeMessages, m)
  102. }
  103. // PREPARE messages
  104. prepareMessages := make([]*qbfttypes.Prepare, 0)
  105. for index, validator := range validatorSet.List() {
  106. var m *qbfttypes.Prepare
  107. if index < preparesForTargetRound {
  108. m = createPrepareMessage(validator.Address(), targetPreparedRound, block)
  109. } else if index >= preparesForTargetRound && index < preparesForTargetRound+preparesNotForTargetRound {
  110. notTargetPreparedRound := targetPreparedRound
  111. for notTargetPreparedRound == targetPreparedRound {
  112. notTargetPreparedRound = rng.Int63()
  113. }
  114. m = createPrepareMessage(validator.Address(), notTargetPreparedRound, block)
  115. } else {
  116. break
  117. }
  118. prepareMessages = append(prepareMessages, m)
  119. }
  120. for _, m := range roundChangeMessages {
  121. fmt.Printf("RC %v\n", m)
  122. }
  123. for _, m := range prepareMessages {
  124. fmt.Printf("PR %v\n", m)
  125. }
  126. fmt.Println("roundChangeMessages", roundChangeMessages, len(roundChangeMessages))
  127. if err := isJustified(block, roundChangeMessages, prepareMessages, quorumSize); err == nil && !messageJustified {
  128. t.Errorf("quorumSize = %v, rcForNil = %v, rcEqualToTargetRound = %v, rcLowerThanTargetRound = %v, rcHigherThanTargetRound = %v, preparesForTargetRound = %v, preparesNotForTargetRound = %v (Expected: %v, Actual: %v)",
  129. quorumSize, rcForNil, rcEqualToTargetRound, rcLowerThanTargetRound, rcHigherThanTargetRound, preparesForTargetRound, preparesNotForTargetRound, err == nil, !messageJustified)
  130. }
  131. }
  132. func createRoundChangeMessage(from common.Address, round int64, preparedRound int64, preparedBlock istanbul.Proposal) *qbfttypes.SignedRoundChangePayload {
  133. m := qbfttypes.NewRoundChange(big.NewInt(1), big.NewInt(1), big.NewInt(preparedRound), preparedBlock)
  134. m.SetSource(from)
  135. return &m.SignedRoundChangePayload
  136. }
  137. func createPrepareMessage(from common.Address, round int64, preparedBlock istanbul.Proposal) *qbfttypes.Prepare {
  138. return qbfttypes.NewPrepareWithSigAndSource(big.NewInt(1), big.NewInt(round), preparedBlock.Hash(), nil, from)
  139. }
  140. func generateValidators(n int) []common.Address {
  141. vals := make([]common.Address, 0)
  142. for i := 0; i < n; i++ {
  143. privateKey, _ := crypto.GenerateKey()
  144. vals = append(vals, crypto.PubkeyToAddress(privateKey.PublicKey))
  145. }
  146. return vals
  147. }
  148. func makeBlock(number int64) *types.Block {
  149. header := &types.Header{
  150. Difficulty: big.NewInt(0),
  151. Number: big.NewInt(number),
  152. GasLimit: 0,
  153. GasUsed: 0,
  154. Time: 0,
  155. }
  156. block := &types.Block{}
  157. return block.WithSeal(header)
  158. }