github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/types/vote_test.go (about)

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