github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/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/tendermint/tendermint/crypto" 12 "github.com/tendermint/tendermint/crypto/ed25519" 13 "github.com/tendermint/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 }