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 }