github.com/project-88388/tendermint-v0.34.14-terra.2@v1.0.0/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/tendermint/tendermint/crypto" 12 "github.com/tendermint/tendermint/crypto/ed25519" 13 "github.com/tendermint/tendermint/crypto/tmhash" 14 "github.com/tendermint/tendermint/libs/protoio" 15 tmproto "github.com/tendermint/tendermint/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 }