github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/types/vote_test.go (about)

     1  package types
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/franono/tendermint/crypto"
    12  	"github.com/franono/tendermint/crypto/ed25519"
    13  	"github.com/franono/tendermint/crypto/tmhash"
    14  )
    15  
    16  func examplePrevote() *Vote {
    17  	return exampleVote(byte(PrevoteType))
    18  }
    19  
    20  func examplePrecommit() *Vote {
    21  	return exampleVote(byte(PrecommitType))
    22  }
    23  
    24  func exampleVote(t byte) *Vote {
    25  	var stamp, err = time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z")
    26  	if err != nil {
    27  		panic(err)
    28  	}
    29  
    30  	return &Vote{
    31  		Type:      SignedMsgType(t),
    32  		Height:    12345,
    33  		Round:     2,
    34  		Timestamp: stamp,
    35  		BlockID: BlockID{
    36  			Hash: tmhash.Sum([]byte("blockID_hash")),
    37  			PartsHeader: PartSetHeader{
    38  				Total: 1000000,
    39  				Hash:  tmhash.Sum([]byte("blockID_part_set_header_hash")),
    40  			},
    41  		},
    42  		ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
    43  		ValidatorIndex:   56789,
    44  	}
    45  }
    46  
    47  func TestVoteSignable(t *testing.T) {
    48  	vote := examplePrecommit()
    49  	signBytes := vote.SignBytes("test_chain_id")
    50  
    51  	expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeVote("test_chain_id", vote))
    52  	require.NoError(t, err)
    53  
    54  	require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Vote.")
    55  }
    56  
    57  func TestVoteSignBytesTestVectors(t *testing.T) {
    58  
    59  	tests := []struct {
    60  		chainID string
    61  		vote    *Vote
    62  		want    []byte
    63  	}{
    64  		0: {
    65  			"", &Vote{},
    66  			// NOTE: Height and Round are skipped here. This case needs to be considered while parsing.
    67  			[]byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1},
    68  		},
    69  		// with proper (fixed size) height and round (PreCommit):
    70  		1: {
    71  			"", &Vote{Height: 1, Round: 1, Type: PrecommitType},
    72  			[]byte{
    73  				0x21,                                   // length
    74  				0x8,                                    // (field_number << 3) | wire_type
    75  				0x2,                                    // PrecommitType
    76  				0x11,                                   // (field_number << 3) | wire_type
    77  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
    78  				0x19,                                   // (field_number << 3) | wire_type
    79  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
    80  				0x2a, // (field_number << 3) | wire_type
    81  				// remaining fields (timestamp):
    82  				0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1},
    83  		},
    84  		// with proper (fixed size) height and round (PreVote):
    85  		2: {
    86  			"", &Vote{Height: 1, Round: 1, Type: PrevoteType},
    87  			[]byte{
    88  				0x21,                                   // length
    89  				0x8,                                    // (field_number << 3) | wire_type
    90  				0x1,                                    // PrevoteType
    91  				0x11,                                   // (field_number << 3) | wire_type
    92  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
    93  				0x19,                                   // (field_number << 3) | wire_type
    94  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
    95  				0x2a, // (field_number << 3) | wire_type
    96  				// remaining fields (timestamp):
    97  				0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1},
    98  		},
    99  		3: {
   100  			"", &Vote{Height: 1, Round: 1},
   101  			[]byte{
   102  				0x1f,                                   // length
   103  				0x11,                                   // (field_number << 3) | wire_type
   104  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
   105  				0x19,                                   // (field_number << 3) | wire_type
   106  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
   107  				// remaining fields (timestamp):
   108  				0x2a,
   109  				0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1},
   110  		},
   111  		// containing non-empty chain_id:
   112  		4: {
   113  			"test_chain_id", &Vote{Height: 1, Round: 1},
   114  			[]byte{
   115  				0x2e,                                   // length
   116  				0x11,                                   // (field_number << 3) | wire_type
   117  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
   118  				0x19,                                   // (field_number << 3) | wire_type
   119  				0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
   120  				// remaining fields:
   121  				0x2a,                                                                // (field_number << 3) | wire_type
   122  				0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp
   123  				// (field_number << 3) | wire_type
   124  				0x32,
   125  				0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64}, // chainID
   126  		},
   127  	}
   128  	for i, tc := range tests {
   129  		got := tc.vote.SignBytes(tc.chainID)
   130  		require.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i)
   131  	}
   132  }
   133  
   134  func TestVoteProposalNotEq(t *testing.T) {
   135  	cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1})
   136  	p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1})
   137  	vb, err := cdc.MarshalBinaryLengthPrefixed(cv)
   138  	require.NoError(t, err)
   139  	pb, err := cdc.MarshalBinaryLengthPrefixed(p)
   140  	require.NoError(t, err)
   141  	require.NotEqual(t, vb, pb)
   142  }
   143  
   144  func TestVoteVerifySignature(t *testing.T) {
   145  	privVal := NewMockPV()
   146  	pubkey, err := privVal.GetPubKey()
   147  	require.NoError(t, err)
   148  
   149  	vote := examplePrecommit()
   150  	signBytes := vote.SignBytes("test_chain_id")
   151  
   152  	// sign it
   153  	err = privVal.SignVote("test_chain_id", vote)
   154  	require.NoError(t, err)
   155  
   156  	// verify the same vote
   157  	valid := pubkey.VerifyBytes(vote.SignBytes("test_chain_id"), vote.Signature)
   158  	require.True(t, valid)
   159  
   160  	// serialize, deserialize and verify again....
   161  	precommit := new(Vote)
   162  	bs, err := cdc.MarshalBinaryLengthPrefixed(vote)
   163  	require.NoError(t, err)
   164  	err = cdc.UnmarshalBinaryLengthPrefixed(bs, &precommit)
   165  	require.NoError(t, err)
   166  
   167  	// verify the transmitted vote
   168  	newSignBytes := precommit.SignBytes("test_chain_id")
   169  	require.Equal(t, string(signBytes), string(newSignBytes))
   170  	valid = pubkey.VerifyBytes(newSignBytes, precommit.Signature)
   171  	require.True(t, valid)
   172  }
   173  
   174  func TestIsVoteTypeValid(t *testing.T) {
   175  	tc := []struct {
   176  		name string
   177  		in   SignedMsgType
   178  		out  bool
   179  	}{
   180  		{"Prevote", PrevoteType, true},
   181  		{"Precommit", PrecommitType, true},
   182  		{"InvalidType", SignedMsgType(0x3), false},
   183  	}
   184  
   185  	for _, tt := range tc {
   186  		tt := tt
   187  		t.Run(tt.name, func(st *testing.T) {
   188  			if rs := IsVoteTypeValid(tt.in); rs != tt.out {
   189  				t.Errorf("got unexpected Vote type. Expected:\n%v\nGot:\n%v", rs, tt.out)
   190  			}
   191  		})
   192  	}
   193  }
   194  
   195  func TestVoteVerify(t *testing.T) {
   196  	privVal := NewMockPV()
   197  	pubkey, err := privVal.GetPubKey()
   198  	require.NoError(t, err)
   199  
   200  	vote := examplePrevote()
   201  	vote.ValidatorAddress = pubkey.Address()
   202  
   203  	err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey())
   204  	if assert.Error(t, err) {
   205  		assert.Equal(t, ErrVoteInvalidValidatorAddress, err)
   206  	}
   207  
   208  	err = vote.Verify("test_chain_id", pubkey)
   209  	if assert.Error(t, err) {
   210  		assert.Equal(t, ErrVoteInvalidSignature, err)
   211  	}
   212  }
   213  
   214  func TestMaxVoteBytes(t *testing.T) {
   215  	// time is varint encoded so need to pick the max.
   216  	// year int, month Month, day, hour, min, sec, nsec int, loc *Location
   217  	timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
   218  
   219  	vote := &Vote{
   220  		ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
   221  		ValidatorIndex:   math.MaxInt64,
   222  		Height:           math.MaxInt64,
   223  		Round:            math.MaxInt64,
   224  		Timestamp:        timestamp,
   225  		Type:             PrevoteType,
   226  		BlockID: BlockID{
   227  			Hash: tmhash.Sum([]byte("blockID_hash")),
   228  			PartsHeader: PartSetHeader{
   229  				Total: math.MaxInt64,
   230  				Hash:  tmhash.Sum([]byte("blockID_part_set_header_hash")),
   231  			},
   232  		},
   233  	}
   234  
   235  	privVal := NewMockPV()
   236  	err := privVal.SignVote("test_chain_id", vote)
   237  	require.NoError(t, err)
   238  
   239  	bz, err := cdc.MarshalBinaryLengthPrefixed(vote)
   240  	require.NoError(t, err)
   241  
   242  	assert.EqualValues(t, MaxVoteBytes, len(bz))
   243  }
   244  
   245  func TestVoteString(t *testing.T) {
   246  	str := examplePrecommit().String()
   247  	expected := `Vote{56789:6AF1F4111082 12345/02/2(Precommit) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}`
   248  	if str != expected {
   249  		t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str)
   250  	}
   251  
   252  	str2 := examplePrevote().String()
   253  	expected = `Vote{56789:6AF1F4111082 12345/02/1(Prevote) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}`
   254  	if str2 != expected {
   255  		t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str2)
   256  	}
   257  }
   258  
   259  func TestVoteValidateBasic(t *testing.T) {
   260  	privVal := NewMockPV()
   261  
   262  	testCases := []struct {
   263  		testName     string
   264  		malleateVote func(*Vote)
   265  		expectErr    bool
   266  	}{
   267  		{"Good Vote", func(v *Vote) {}, false},
   268  		{"Negative Height", func(v *Vote) { v.Height = -1 }, true},
   269  		{"Negative Round", func(v *Vote) { v.Round = -1 }, true},
   270  		{"Invalid BlockID", func(v *Vote) {
   271  			v.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}}
   272  		}, true},
   273  		{"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true},
   274  		{"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true},
   275  		{"Invalid Signature", func(v *Vote) { v.Signature = nil }, true},
   276  		{"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true},
   277  	}
   278  	for _, tc := range testCases {
   279  		tc := tc
   280  		t.Run(tc.testName, func(t *testing.T) {
   281  			vote := examplePrecommit()
   282  			err := privVal.SignVote("test_chain_id", vote)
   283  			require.NoError(t, err)
   284  			tc.malleateVote(vote)
   285  			assert.Equal(t, tc.expectErr, vote.ValidateBasic() != nil, "Validate Basic had an unexpected result")
   286  		})
   287  	}
   288  }
   289  
   290  func TestVoteProtobuf(t *testing.T) {
   291  	privVal := NewMockPV()
   292  	vote := examplePrecommit()
   293  	err := privVal.SignVote("test_chain_id", vote)
   294  	require.NoError(t, err)
   295  
   296  	testCases := []struct {
   297  		msg     string
   298  		v1      *Vote
   299  		expPass bool
   300  	}{
   301  		{"success", vote, true},
   302  		{"fail vote validate basic", &Vote{}, false},
   303  		{"failure nil", nil, false},
   304  	}
   305  	for _, tc := range testCases {
   306  		protoProposal := tc.v1.ToProto()
   307  
   308  		v, err := VoteFromProto(protoProposal)
   309  		if tc.expPass {
   310  			require.NoError(t, err)
   311  			require.Equal(t, tc.v1, v, tc.msg)
   312  		} else {
   313  			require.Error(t, err)
   314  		}
   315  	}
   316  }