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.

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