- package e2e_test
-
- import (
- "bytes"
- "context"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- e2e "github.com/tendermint/tendermint/test/e2e/pkg"
- "github.com/tendermint/tendermint/types"
- )
-
- // Tests that validator sets are available and correct according to
- // scheduled validator updates.
- func TestValidator_Sets(t *testing.T) {
- testNode(t, func(ctx context.Context, t *testing.T, node e2e.Node) {
- client, err := node.Client()
- require.NoError(t, err)
- status, err := client.Status(ctx)
- require.NoError(t, err)
-
- first := status.SyncInfo.EarliestBlockHeight
-
- // for nodes that have to catch up, we should only
- // check the validator sets for nodes after this
- // point, to avoid inconsistencies with backfill.
- if node.StartAt > first {
- first = node.StartAt
- }
-
- last := status.SyncInfo.LatestBlockHeight
-
- // skip first block if node is pruning blocks, to avoid race conditions
- if node.RetainBlocks > 0 {
- first++
- }
-
- valSchedule := newValidatorSchedule(*node.Testnet)
- require.NoError(t, valSchedule.Increment(first-node.Testnet.InitialHeight))
-
- for h := first; h <= last; h++ {
- validators := []*types.Validator{}
- perPage := 100
- for page := 1; ; page++ {
- resp, err := client.Validators(ctx, &(h), &(page), &perPage)
- require.NoError(t, err)
- validators = append(validators, resp.Validators...)
- if len(validators) == resp.Total {
- break
- }
- }
- require.Equal(t, valSchedule.Set.Validators, validators,
- "incorrect validator set at height %v", h)
- require.NoError(t, valSchedule.Increment(1))
- }
- })
- }
-
- // Tests that a validator proposes blocks when it's supposed to. It tolerates some
- // missed blocks, e.g. due to testnet perturbations.
- func TestValidator_Propose(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- blocks := fetchBlockChain(ctx, t)
- testNode(t, func(ctx context.Context, t *testing.T, node e2e.Node) {
- if node.Mode != e2e.ModeValidator {
- return
- }
- address := node.PrivvalKey.PubKey().Address()
- valSchedule := newValidatorSchedule(*node.Testnet)
-
- expectCount := 0
- proposeCount := 0
- for _, block := range blocks {
- if bytes.Equal(valSchedule.Set.Proposer.Address, address) {
- expectCount++
- if bytes.Equal(block.ProposerAddress, address) {
- proposeCount++
- }
- }
- require.NoError(t, valSchedule.Increment(1))
- }
-
- require.False(t, proposeCount == 0 && expectCount > 0,
- "node did not propose any blocks (expected %v)", expectCount)
- if expectCount > 5 {
- require.GreaterOrEqual(t, proposeCount, 3, "validator didn't propose even 3 blocks")
- }
- })
- }
-
- // Tests that a validator signs blocks when it's supposed to. It tolerates some
- // missed blocks, e.g. due to testnet perturbations.
- func TestValidator_Sign(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- blocks := fetchBlockChain(ctx, t)
- testNode(t, func(ctx context.Context, t *testing.T, node e2e.Node) {
- if node.Mode != e2e.ModeValidator {
- return
- }
- address := node.PrivvalKey.PubKey().Address()
- valSchedule := newValidatorSchedule(*node.Testnet)
-
- expectCount := 0
- signCount := 0
- for _, block := range blocks[1:] { // Skip first block, since it has no signatures
- signed := false
- for _, sig := range block.LastCommit.Signatures {
- if bytes.Equal(sig.ValidatorAddress, address) {
- signed = true
- break
- }
- }
- if valSchedule.Set.HasAddress(address) {
- expectCount++
- if signed {
- signCount++
- }
- } else {
- require.False(t, signed, "unexpected signature for block %v", block.LastCommit.Height)
- }
- require.NoError(t, valSchedule.Increment(1))
- }
-
- require.False(t, signCount == 0 && expectCount > 0,
- "validator did not sign any blocks (expected %v)", expectCount)
- if expectCount > 7 {
- require.GreaterOrEqual(t, signCount, 3, "validator didn't sign even 3 blocks (expected %v)", expectCount)
- }
- })
- }
-
- // validatorSchedule is a validator set iterator, which takes into account
- // validator set updates.
- type validatorSchedule struct {
- Set *types.ValidatorSet
- height int64
- updates map[int64]map[*e2e.Node]int64
- }
-
- func newValidatorSchedule(testnet e2e.Testnet) *validatorSchedule {
- valMap := testnet.Validators // genesis validators
- if v, ok := testnet.ValidatorUpdates[0]; ok { // InitChain validators
- valMap = v
- }
- return &validatorSchedule{
- height: testnet.InitialHeight,
- Set: types.NewValidatorSet(makeVals(valMap)),
- updates: testnet.ValidatorUpdates,
- }
- }
-
- func (s *validatorSchedule) Increment(heights int64) error {
- for i := int64(0); i < heights; i++ {
- s.height++
- if s.height > 2 {
- // validator set updates are offset by 2, since they only take effect
- // two blocks after they're returned.
- if update, ok := s.updates[s.height-2]; ok {
- if err := s.Set.UpdateWithChangeSet(makeVals(update)); err != nil {
- return err
- }
- }
- }
- s.Set.IncrementProposerPriority(1)
- }
- return nil
- }
-
- func makeVals(valMap map[*e2e.Node]int64) []*types.Validator {
- vals := make([]*types.Validator, 0, len(valMap))
- for node, power := range valMap {
- vals = append(vals, types.NewValidator(node.PrivvalKey.PubKey(), power))
- }
- return vals
- }
|