github.com/DFWallet/tendermint-cosmos@v0.0.2/types/vote_test.go (about)

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