github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/voter/voter_test.go (about) 1 package voter 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/mock" 7 "github.com/stretchr/testify/require" 8 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/helper" 10 "github.com/koko1123/flow-go-1/consensus/hotstuff/mocks" 11 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 12 "github.com/koko1123/flow-go-1/utils/unittest" 13 ) 14 15 func TestProduceVote(t *testing.T) { 16 t.Run("should vote for block", testVoterOK) 17 t.Run("should not vote for unsafe block", testUnsafe) 18 t.Run("should not vote for block with its view below the current view", testBelowVote) 19 t.Run("should not vote for block with its view above the current view", testAboveVote) 20 t.Run("should not vote for block with the same view as the last voted view", testEqualLastVotedView) 21 t.Run("should not vote for block with its view below the last voted view", testBelowLastVotedView) 22 t.Run("should not vote for the same view again", testVotingAgain) 23 t.Run("should not vote while not a committee member", testVotingWhileNonCommitteeMember) 24 } 25 26 func createVoter(blockView uint64, lastVotedView uint64, isBlockSafe, isCommitteeMember bool) (*model.Block, *model.Vote, *Voter) { 27 block := helper.MakeBlock(helper.WithBlockView(blockView)) 28 expectVote := makeVote(block) 29 30 forks := &mocks.ForksReader{} 31 forks.On("IsSafeBlock", block).Return(isBlockSafe) 32 33 persist := &mocks.Persister{} 34 persist.On("PutVoted", mock.Anything).Return(nil) 35 36 signer := &mocks.Signer{} 37 signer.On("CreateVote", mock.Anything).Return(expectVote, nil) 38 39 committee := &mocks.Committee{} 40 me := unittest.IdentityFixture() 41 committee.On("Self").Return(me.NodeID, nil) 42 if isCommitteeMember { 43 committee.On("Identity", mock.Anything, me.NodeID).Return(me, nil) 44 } else { 45 committee.On("Identity", mock.Anything, me.NodeID).Return(nil, model.NewInvalidSignerErrorf("")) 46 } 47 48 voter := New(signer, forks, persist, committee, lastVotedView) 49 return block, expectVote, voter 50 } 51 52 func testVoterOK(t *testing.T) { 53 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(2), true, true 54 55 // create voter 56 block, expectVote, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 57 58 // produce vote 59 vote, err := voter.ProduceVoteIfVotable(block, curView) 60 61 require.NoError(t, err) 62 require.Equal(t, vote, expectVote) 63 } 64 65 func testUnsafe(t *testing.T) { 66 // create unsafe block 67 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(2), false, true 68 69 // create voter 70 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 71 72 _, err := voter.ProduceVoteIfVotable(block, curView) 73 require.Error(t, err) 74 require.Contains(t, err.Error(), "not safe") 75 require.True(t, model.IsNoVoteError(err)) 76 } 77 78 func testBelowVote(t *testing.T) { 79 // curView < blockView 80 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(2), uint64(2), true, true 81 82 // create voter 83 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 84 85 _, err := voter.ProduceVoteIfVotable(block, curView) 86 require.Error(t, err) 87 require.Contains(t, err.Error(), "expecting block for current view") 88 require.False(t, model.IsNoVoteError(err)) 89 } 90 91 func testAboveVote(t *testing.T) { 92 // curView > blockView 93 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(4), uint64(2), true, true 94 95 // create voter 96 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 97 98 _, err := voter.ProduceVoteIfVotable(block, curView) 99 require.Error(t, err) 100 require.Contains(t, err.Error(), "expecting block for current view") 101 require.False(t, model.IsNoVoteError(err)) 102 } 103 104 func testEqualLastVotedView(t *testing.T) { 105 // curView == lastVotedView 106 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(3), true, true 107 108 // create voter 109 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 110 111 _, err := voter.ProduceVoteIfVotable(block, curView) 112 require.Error(t, err) 113 require.Contains(t, err.Error(), "must be larger than the last voted view") 114 require.False(t, model.IsNoVoteError(err)) 115 } 116 117 func testBelowLastVotedView(t *testing.T) { 118 // curView < lastVotedView 119 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(4), true, true 120 121 // create voter 122 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 123 124 _, err := voter.ProduceVoteIfVotable(block, curView) 125 require.Error(t, err) 126 require.Contains(t, err.Error(), "must be larger than the last voted view") 127 require.False(t, model.IsNoVoteError(err)) 128 } 129 130 func testVotingAgain(t *testing.T) { 131 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(2), true, true 132 133 // create voter 134 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 135 136 // produce vote 137 _, err := voter.ProduceVoteIfVotable(block, curView) 138 require.NoError(t, err) 139 140 // produce vote again for the same view 141 _, err = voter.ProduceVoteIfVotable(block, curView) 142 require.Error(t, err) 143 require.Contains(t, err.Error(), "must be larger than the last voted view") 144 require.False(t, model.IsNoVoteError(err)) 145 } 146 147 func testVotingWhileNonCommitteeMember(t *testing.T) { 148 blockView, curView, lastVotedView, isBlockSafe, isCommitteeMember := uint64(3), uint64(3), uint64(2), true, false 149 150 // create voter 151 block, _, voter := createVoter(blockView, lastVotedView, isBlockSafe, isCommitteeMember) 152 153 // produce vote 154 _, err := voter.ProduceVoteIfVotable(block, curView) 155 156 require.Error(t, err) 157 require.True(t, model.IsNoVoteError(err)) 158 } 159 160 func makeVote(block *model.Block) *model.Vote { 161 return &model.Vote{ 162 BlockID: block.BlockID, 163 View: block.View, 164 SigData: nil, // signature doesn't matter in this test case 165 } 166 }