package consensus import ( "bytes" "testing" "github.com/tendermint/tendermint/types" ) func TestSetupRound(t *testing.T) { cs, privValidators := randConsensusState() val0 := privValidators[0] // Add a vote, precommit, and commit by val0. voteTypes := []byte{types.VoteTypePrevote, types.VoteTypePrecommit, types.VoteTypeCommit} for _, voteType := range voteTypes { vote := &types.Vote{Height: 1, Round: 0, Type: voteType} // nil vote err := val0.SignVote(vote) if err != nil { t.Error("Error signing vote: %v", err) } cs.AddVote(val0.Address, vote) } // Ensure that vote appears in RoundState. rs0 := cs.GetRoundState() if vote := rs0.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrevote { t.Errorf("Expected to find prevote but got %v", vote) } if vote := rs0.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrecommit { t.Errorf("Expected to find precommit but got %v", vote) } if vote := rs0.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { t.Errorf("Expected to find commit but got %v", vote) } // Setup round 1 (next round) cs.SetupNewRound(1, 1) <-cs.NewStepCh() // Now the commit should be copied over to prevotes and precommits. rs1 := cs.GetRoundState() if vote := rs1.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { t.Errorf("Expected to find commit but got %v", vote) } if vote := rs1.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { t.Errorf("Expected to find commit but got %v", vote) } if vote := rs1.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { t.Errorf("Expected to find commit but got %v", vote) } } func TestRunActionProposeNoPrivValidator(t *testing.T) { cs, _ := randConsensusState() cs.RunActionPropose(1, 0) rs := cs.GetRoundState() if rs.Proposal != nil { t.Error("Expected to make no proposal, since no privValidator") } } func TestRunActionPropose(t *testing.T) { cs, privValidators := randConsensusState() val0 := privValidators[0] cs.SetPrivValidator(val0) cs.RunActionPropose(1, 0) rs := cs.GetRoundState() // Check that Proposal, ProposalBlock, ProposalBlockParts are set. if rs.Proposal == nil { t.Error("rs.Proposal should be set") } if rs.ProposalBlock == nil { t.Error("rs.ProposalBlock should be set") } if rs.ProposalBlockParts.Total() == 0 { t.Error("rs.ProposalBlockParts should be set") } } func checkRoundState(t *testing.T, rs *RoundState, height uint, round uint, step RoundStepType) { if rs.Height != height { t.Errorf("rs.Height should be %v, got %v", height, rs.Height) } if rs.Round != round { t.Errorf("rs.Round should be %v, got %v", round, rs.Round) } if rs.Step != step { t.Errorf("rs.Step should be %v, got %v", step, rs.Step) } } func TestRunActionPrecommitCommitFinalize(t *testing.T) { cs, privValidators := randConsensusState() val0 := privValidators[0] cs.SetPrivValidator(val0) cs.RunActionPrecommit(1, 0) <-cs.NewStepCh() // TODO: test this value too. if cs.Precommits.GetByAddress(val0.Address) != nil { t.Errorf("RunActionPrecommit should return nil without a proposal") } cs.RunActionPropose(1, 0) <-cs.NewStepCh() // TODO: test this value too. cs.RunActionPrecommit(1, 0) <-cs.NewStepCh() // TODO: test this value too. if cs.Precommits.GetByAddress(val0.Address) != nil { t.Errorf("RunActionPrecommit should return nil, not enough prevotes") } // Add at least +2/3 prevotes. for i := 0; i < 7; i++ { vote := &types.Vote{ Height: 1, Round: 0, Type: types.VoteTypePrevote, BlockHash: cs.ProposalBlock.Hash(), BlockParts: cs.ProposalBlockParts.Header(), } err := privValidators[i].SignVote(vote) if err != nil { t.Error("Error signing vote: %v", err) } cs.AddVote(privValidators[i].Address, vote) } // Test RunActionPrecommit success: cs.RunActionPrecommit(1, 0) <-cs.NewStepCh() // TODO: test this value too. if cs.Precommits.GetByAddress(val0.Address) == nil { t.Errorf("RunActionPrecommit should have succeeded") } checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepPrecommit) // Add at least +2/3 precommits. for i := 0; i < 7; i++ { if bytes.Equal(privValidators[i].Address, val0.Address) { if cs.Precommits.GetByAddress(val0.Address) == nil { t.Errorf("Proposer should already have signed a precommit vote") } continue } vote := &types.Vote{ Height: 1, Round: 0, Type: types.VoteTypePrecommit, BlockHash: cs.ProposalBlock.Hash(), BlockParts: cs.ProposalBlockParts.Header(), } err := privValidators[i].SignVote(vote) if err != nil { t.Error("Error signing vote: %v", err) } added, _, err := cs.AddVote(privValidators[i].Address, vote) if !added || err != nil { t.Errorf("Error adding precommit: %v", err) } } // Test RunActionCommit success: cs.RunActionCommit(1) <-cs.NewStepCh() // TODO: test this value too. if cs.Commits.GetByAddress(val0.Address) == nil { t.Errorf("RunActionCommit should have succeeded") } checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepCommit) // cs.CommitTime should still be zero if !cs.CommitTime.IsZero() { t.Errorf("Expected CommitTime to yet be zero") } // Add at least +2/3 commits. for i := 0; i < 7; i++ { if bytes.Equal(privValidators[i].Address, val0.Address) { if cs.Commits.GetByAddress(val0.Address) == nil { t.Errorf("Proposer should already have signed a commit vote") } continue } vote := &types.Vote{ Height: 1, Round: uint(i), // Doesn't matter what round Type: types.VoteTypeCommit, BlockHash: cs.ProposalBlock.Hash(), BlockParts: cs.ProposalBlockParts.Header(), } err := privValidators[i].SignVote(vote) if err != nil { t.Error("Error signing vote: %v", err) } added, _, err := cs.AddVote(privValidators[i].Address, vote) if !added || err != nil { t.Errorf("Error adding commit: %v", err) } } // Test TryFinalizeCommit: cs.TryFinalizeCommit(1) <-cs.NewStepCh() // TODO: test this value too. checkRoundState(t, cs.GetRoundState(), 2, 0, RoundStepNewHeight) }