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  }