You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.7 KiB

  1. package e2e_test
  2. import (
  3. "bytes"
  4. "testing"
  5. "github.com/stretchr/testify/require"
  6. e2e "github.com/tendermint/tendermint/test/e2e/pkg"
  7. "github.com/tendermint/tendermint/types"
  8. )
  9. // Tests that validator sets are available and correct according to
  10. // scheduled validator updates.
  11. func TestValidator_Sets(t *testing.T) {
  12. testNode(t, func(t *testing.T, node e2e.Node) {
  13. if node.Mode == e2e.ModeSeed {
  14. return
  15. }
  16. client, err := node.Client()
  17. require.NoError(t, err)
  18. status, err := client.Status(ctx)
  19. require.NoError(t, err)
  20. first := status.SyncInfo.EarliestBlockHeight
  21. last := status.SyncInfo.LatestBlockHeight
  22. // skip first block if node is pruning blocks, to avoid race conditions
  23. if node.RetainBlocks > 0 {
  24. first++
  25. }
  26. valSchedule := newValidatorSchedule(*node.Testnet)
  27. valSchedule.Increment(first - node.Testnet.InitialHeight)
  28. for h := first; h <= last; h++ {
  29. validators := []*types.Validator{}
  30. perPage := 100
  31. for page := 1; ; page++ {
  32. resp, err := client.Validators(ctx, &(h), &(page), &perPage)
  33. require.NoError(t, err)
  34. validators = append(validators, resp.Validators...)
  35. if len(validators) == resp.Total {
  36. break
  37. }
  38. }
  39. require.Equal(t, valSchedule.Set.Validators, validators,
  40. "incorrect validator set at height %v", h)
  41. valSchedule.Increment(1)
  42. }
  43. })
  44. }
  45. // Tests that a validator proposes blocks when it's supposed to. It tolerates some
  46. // missed blocks, e.g. due to testnet perturbations.
  47. func TestValidator_Propose(t *testing.T) {
  48. blocks := fetchBlockChain(t)
  49. testNode(t, func(t *testing.T, node e2e.Node) {
  50. if node.Mode != e2e.ModeValidator {
  51. return
  52. }
  53. address := node.PrivvalKey.PubKey().Address()
  54. valSchedule := newValidatorSchedule(*node.Testnet)
  55. expectCount := 0
  56. proposeCount := 0
  57. for _, block := range blocks {
  58. if bytes.Equal(valSchedule.Set.Proposer.Address, address) {
  59. expectCount++
  60. if bytes.Equal(block.ProposerAddress, address) {
  61. proposeCount++
  62. }
  63. }
  64. valSchedule.Increment(1)
  65. }
  66. require.False(t, proposeCount == 0 && expectCount > 0,
  67. "node did not propose any blocks (expected %v)", expectCount)
  68. require.Less(t, expectCount-proposeCount, 5,
  69. "validator missed proposing too many blocks (proposed %v out of %v)", proposeCount, expectCount)
  70. })
  71. }
  72. // Tests that a validator signs blocks when it's supposed to. It tolerates some
  73. // missed blocks, e.g. due to testnet perturbations.
  74. func TestValidator_Sign(t *testing.T) {
  75. blocks := fetchBlockChain(t)
  76. testNode(t, func(t *testing.T, node e2e.Node) {
  77. if node.Mode != e2e.ModeValidator {
  78. return
  79. }
  80. address := node.PrivvalKey.PubKey().Address()
  81. valSchedule := newValidatorSchedule(*node.Testnet)
  82. expectCount := 0
  83. signCount := 0
  84. for _, block := range blocks[1:] { // Skip first block, since it has no signatures
  85. signed := false
  86. for _, sig := range block.LastCommit.Signatures {
  87. if bytes.Equal(sig.ValidatorAddress, address) {
  88. signed = true
  89. break
  90. }
  91. }
  92. if valSchedule.Set.HasAddress(address) {
  93. expectCount++
  94. if signed {
  95. signCount++
  96. }
  97. } else {
  98. require.False(t, signed, "unexpected signature for block %v", block.LastCommit.Height)
  99. }
  100. valSchedule.Increment(1)
  101. }
  102. require.False(t, signCount == 0 && expectCount > 0,
  103. "validator did not sign any blocks (expected %v)", expectCount)
  104. require.Less(t, float64(expectCount-signCount)/float64(expectCount), 0.33,
  105. "validator missed signing too many blocks (signed %v out of %v)", signCount, expectCount)
  106. })
  107. }
  108. // validatorSchedule is a validator set iterator, which takes into account
  109. // validator set updates.
  110. type validatorSchedule struct {
  111. Set *types.ValidatorSet
  112. height int64
  113. updates map[int64]map[*e2e.Node]int64
  114. }
  115. func newValidatorSchedule(testnet e2e.Testnet) *validatorSchedule {
  116. valMap := testnet.Validators // genesis validators
  117. if v, ok := testnet.ValidatorUpdates[0]; ok { // InitChain validators
  118. valMap = v
  119. }
  120. return &validatorSchedule{
  121. height: testnet.InitialHeight,
  122. Set: types.NewValidatorSet(makeVals(valMap)),
  123. updates: testnet.ValidatorUpdates,
  124. }
  125. }
  126. func (s *validatorSchedule) Increment(heights int64) {
  127. for i := int64(0); i < heights; i++ {
  128. s.height++
  129. if s.height > 2 {
  130. // validator set updates are offset by 2, since they only take effect
  131. // two blocks after they're returned.
  132. if update, ok := s.updates[s.height-2]; ok {
  133. if err := s.Set.UpdateWithChangeSet(makeVals(update)); err != nil {
  134. panic(err)
  135. }
  136. }
  137. }
  138. s.Set.IncrementProposerPriority(1)
  139. }
  140. }
  141. func makeVals(valMap map[*e2e.Node]int64) []*types.Validator {
  142. vals := make([]*types.Validator, 0, len(valMap))
  143. for node, power := range valMap {
  144. vals = append(vals, types.NewValidator(node.PrivvalKey.PubKey(), power))
  145. }
  146. return vals
  147. }