github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/types/evidence_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 tmrand "github.com/tendermint/tendermint/libs/rand" 15 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 16 ) 17 18 type voteData struct { 19 vote1 *Vote 20 vote2 *Vote 21 valid bool 22 } 23 24 var defaultVoteTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 25 26 func TestEvidence(t *testing.T) { 27 val := NewMockPV() 28 val2 := NewMockPV() 29 30 blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) 31 blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) 32 blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash")) 33 blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2")) 34 35 const chainID = "mychain" 36 37 vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime) 38 v1 := vote1.ToProto() 39 err := val.SignVote(chainID, v1) 40 require.NoError(t, err) 41 badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime) 42 bv := badVote.ToProto() 43 err = val2.SignVote(chainID, bv) 44 require.NoError(t, err) 45 46 vote1.Signature = v1.Signature 47 badVote.Signature = bv.Signature 48 49 cases := []voteData{ 50 {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime), true}, // different block ids 51 {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3, defaultVoteTime), true}, 52 {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4, defaultVoteTime), true}, 53 {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), false}, // wrong block id 54 {vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2, defaultVoteTime), false}, // wrong chain id 55 {vote1, makeVote(t, val, chainID, 1, 10, 2, 1, blockID2, defaultVoteTime), false}, // wrong val index 56 {vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2, defaultVoteTime), false}, // wrong height 57 {vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2, defaultVoteTime), false}, // wrong round 58 {vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2, defaultVoteTime), false}, // wrong step 59 {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), false}, // wrong validator 60 {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID, time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), false}, 61 {vote1, badVote, false}, // signed by wrong key 62 } 63 64 pubKey, err := val.GetPubKey() 65 require.NoError(t, err) 66 for _, c := range cases { 67 ev := &DuplicateVoteEvidence{ 68 VoteA: c.vote1, 69 VoteB: c.vote2, 70 } 71 if c.valid { 72 assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid") 73 } else { 74 assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid") 75 } 76 } 77 } 78 79 func TestDuplicatedVoteEvidence(t *testing.T) { 80 ev := randomDuplicatedVoteEvidence(t) 81 82 assert.True(t, ev.Equal(ev)) 83 assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) 84 85 maxTime := ev.VoteB.Timestamp 86 if ev.VoteA.Timestamp.After(ev.VoteB.Timestamp) { 87 maxTime = ev.VoteA.Timestamp 88 } 89 assert.Equal(t, maxTime, ev.Time(), "expected time of the latest vote") 90 } 91 92 func TestEvidenceList(t *testing.T) { 93 ev := randomDuplicatedVoteEvidence(t) 94 evl := EvidenceList([]Evidence{ev}) 95 96 assert.NotNil(t, evl.Hash()) 97 assert.True(t, evl.Has(ev)) 98 assert.False(t, evl.Has(&DuplicateVoteEvidence{})) 99 } 100 101 func TestMaxEvidenceBytes(t *testing.T) { 102 val := NewMockPV() 103 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 104 blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 105 maxTime := time.Date(9999, 0, 0, 0, 0, 0, 0, time.UTC) 106 const chainID = "mychain" 107 ev := &DuplicateVoteEvidence{ 108 VoteA: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID, maxTime), 109 VoteB: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID2, maxTime), 110 } 111 112 //TODO: Add other types of evidence to test and set MaxEvidenceBytes accordingly 113 114 // evl := &LunaticValidatorEvidence{ 115 // Header: makeHeaderRandom(), 116 // Vote: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), 117 118 // InvalidHeaderField: "", 119 // } 120 121 // evp := &PhantomValidatorEvidence{ 122 // Header: makeHeaderRandom(), 123 // Vote: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), 124 125 // LastHeightValidatorWasInSet: math.MaxInt64, 126 // } 127 128 // signedHeader := SignedHeader{Header: makeHeaderRandom(), Commit: randCommit(time.Now())} 129 // evc := &ConflictingHeadersEvidence{ 130 // H1: &signedHeader, 131 // H2: &signedHeader, 132 // } 133 134 testCases := []struct { 135 testName string 136 evidence Evidence 137 }{ 138 {"DuplicateVote", ev}, 139 // {"LunaticValidatorEvidence", evl}, 140 // {"PhantomValidatorEvidence", evp}, 141 // {"ConflictingHeadersEvidence", evc}, 142 } 143 144 for _, tt := range testCases { 145 pb, err := EvidenceToProto(tt.evidence) 146 require.NoError(t, err, tt.testName) 147 bz, err := pb.Marshal() 148 require.NoError(t, err, tt.testName) 149 150 assert.LessOrEqual(t, int64(len(bz)), MaxEvidenceBytes, tt.testName) 151 } 152 153 } 154 155 func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence { 156 val := NewMockPV() 157 blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) 158 blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) 159 const chainID = "mychain" 160 return &DuplicateVoteEvidence{ 161 VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), 162 VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime.Add(1*time.Minute)), 163 } 164 } 165 166 func TestDuplicateVoteEvidenceValidation(t *testing.T) { 167 val := NewMockPV() 168 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 169 blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 170 const chainID = "mychain" 171 172 testCases := []struct { 173 testName string 174 malleateEvidence func(*DuplicateVoteEvidence) 175 expectErr bool 176 }{ 177 {"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false}, 178 {"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true}, 179 {"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true}, 180 {"Nil votes", func(ev *DuplicateVoteEvidence) { 181 ev.VoteA = nil 182 ev.VoteB = nil 183 }, true}, 184 {"Invalid vote type", func(ev *DuplicateVoteEvidence) { 185 ev.VoteA = makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0, blockID2, defaultVoteTime) 186 }, true}, 187 {"Invalid vote order", func(ev *DuplicateVoteEvidence) { 188 swap := ev.VoteA.Copy() 189 ev.VoteA = ev.VoteB.Copy() 190 ev.VoteB = swap 191 }, true}, 192 } 193 for _, tc := range testCases { 194 tc := tc 195 t.Run(tc.testName, func(t *testing.T) { 196 vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime) 197 vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime) 198 ev := NewDuplicateVoteEvidence(vote1, vote2) 199 tc.malleateEvidence(ev) 200 assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") 201 }) 202 } 203 } 204 205 func TestMockEvidenceValidateBasic(t *testing.T) { 206 goodEvidence := NewMockDuplicateVoteEvidence(int64(1), time.Now(), "mock-chain-id") 207 assert.Nil(t, goodEvidence.ValidateBasic()) 208 } 209 210 func TestLunaticValidatorEvidence(t *testing.T) { 211 var ( 212 invalidBlockID = makeBlockIDRandom() 213 header = makeHeaderRandom() 214 altHeader = makeHeaderRandom() 215 bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") 216 val = NewMockPV() 217 ) 218 219 header.Time = bTime 220 221 blockID := BlockID{ 222 Hash: header.Hash(), 223 PartSetHeader: PartSetHeader{ 224 Total: 100, 225 Hash: crypto.CRandBytes(tmhash.Size), 226 }, 227 } 228 229 vote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime) 230 231 ev := NewLunaticValidatorEvidence(header, vote, "AppHash") 232 233 //happy path 234 assert.Equal(t, header.Height, ev.Height()) 235 assert.Equal(t, defaultVoteTime, ev.Time()) 236 assert.EqualValues(t, vote.ValidatorAddress, ev.Address()) 237 assert.NotEmpty(t, ev.Hash()) 238 assert.NotEmpty(t, ev.Bytes()) 239 assert.True(t, ev.Equal(ev)) 240 pubKey, err := val.GetPubKey() 241 require.NoError(t, err) 242 assert.NoError(t, ev.Verify(header.ChainID, pubKey)) 243 assert.NoError(t, ev.ValidateBasic()) 244 assert.NotEmpty(t, ev.String()) 245 assert.NoError(t, ev.VerifyHeader(altHeader)) 246 247 // invalid evidence 248 assert.Error(t, ev.Verify("other", pubKey)) 249 privKey2 := ed25519.GenPrivKey() 250 pubKey2 := privKey2.PubKey() 251 assert.Error(t, ev.Verify(header.ChainID, pubKey2)) 252 assert.Error(t, ev.VerifyHeader(header)) 253 254 invalidVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, invalidBlockID, defaultVoteTime) 255 invalidHeightVote := makeVote(t, val, header.ChainID, 0, header.Height+1, 0, 2, blockID, defaultVoteTime) 256 emptyBlockVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, BlockID{}, defaultVoteTime) 257 258 invalidLunaticEvidence := []*LunaticValidatorEvidence{ 259 NewLunaticValidatorEvidence(header, invalidVote, "AppHash"), 260 NewLunaticValidatorEvidence(header, invalidHeightVote, "AppHash"), 261 NewLunaticValidatorEvidence(nil, vote, "AppHash"), 262 NewLunaticValidatorEvidence(header, nil, "AppHash"), 263 NewLunaticValidatorEvidence(header, vote, "other"), 264 NewLunaticValidatorEvidence(header, emptyBlockVote, "AppHash"), 265 } 266 267 for idx, ev := range invalidLunaticEvidence { 268 assert.Error(t, ev.ValidateBasic(), "#%d", idx) 269 } 270 271 } 272 273 func TestPhantomValidatorEvidence(t *testing.T) { 274 var ( 275 blockID = makeBlockIDRandom() 276 header = makeHeaderRandom() 277 val = NewMockPV() 278 vote = makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime) 279 ) 280 281 ev := NewPhantomValidatorEvidence(vote, header.Height-1) 282 283 assert.Equal(t, header.Height, ev.Height()) 284 assert.Equal(t, defaultVoteTime, ev.Time()) 285 assert.EqualValues(t, vote.ValidatorAddress, ev.Address()) 286 assert.NotEmpty(t, ev.Hash()) 287 assert.NotEmpty(t, ev.Bytes()) 288 pubKey, err := val.GetPubKey() 289 require.NoError(t, err) 290 assert.NoError(t, ev.Verify(header.ChainID, pubKey)) 291 assert.Error(t, ev.Verify("other", pubKey)) 292 privKey2 := ed25519.GenPrivKey() 293 pubKey2 := privKey2.PubKey() 294 assert.Error(t, ev.Verify("other", pubKey2)) 295 assert.True(t, ev.Equal(ev)) 296 assert.NoError(t, ev.ValidateBasic()) 297 assert.NotEmpty(t, ev.String()) 298 } 299 300 func TestConflictingHeadersEvidence(t *testing.T) { 301 const ( 302 chainID = "TestConflictingHeadersEvidence" 303 height int64 = 37 304 ) 305 306 var ( 307 blockID = makeBlockIDRandom() 308 header1 = makeHeaderRandom() 309 header2 = makeHeaderRandom() 310 ) 311 312 header1.Height = height 313 header1.LastBlockID = blockID 314 header1.ChainID = chainID 315 316 header2.Height = height 317 header2.LastBlockID = blockID 318 header2.ChainID = chainID 319 320 voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) 321 voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet) 322 323 commit1, err := MakeCommit(BlockID{ 324 Hash: header1.Hash(), 325 PartSetHeader: PartSetHeader{ 326 Total: 100, 327 Hash: crypto.CRandBytes(tmhash.Size), 328 }, 329 }, height, 1, voteSet1, vals, time.Now()) 330 require.NoError(t, err) 331 commit2, err := MakeCommit(BlockID{ 332 Hash: header2.Hash(), 333 PartSetHeader: PartSetHeader{ 334 Total: 100, 335 Hash: crypto.CRandBytes(tmhash.Size), 336 }, 337 }, height, 1, voteSet2, vals, time.Now()) 338 require.NoError(t, err) 339 340 h1 := &SignedHeader{ 341 Header: header1, 342 Commit: commit1, 343 } 344 h2 := &SignedHeader{ 345 Header: header2, 346 Commit: commit2, 347 } 348 349 ev := NewConflictingHeadersEvidence(h1, h2) 350 351 assert.Panics(t, func() { 352 ev.Address() 353 }) 354 355 assert.Panics(t, func() { 356 pubKey, _ := vals[0].GetPubKey() 357 ev.Verify(chainID, pubKey) 358 }) 359 360 assert.Equal(t, height, ev.Height()) 361 assert.Equal(t, ev.H2.Time, ev.Time()) 362 assert.NotEmpty(t, ev.Hash()) 363 assert.NotEmpty(t, ev.Bytes()) 364 assert.NoError(t, ev.VerifyComposite(header1, valSet)) 365 assert.True(t, ev.Equal(ev)) 366 assert.NoError(t, ev.ValidateBasic()) 367 assert.NotEmpty(t, ev.String()) 368 } 369 370 func TestPotentialAmnesiaEvidence(t *testing.T) { 371 const ( 372 chainID = "TestPotentialAmnesiaEvidence" 373 height int64 = 37 374 ) 375 376 var ( 377 val = NewMockPV() 378 blockID = makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 379 blockID2 = makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 380 vote1 = makeVote(t, val, chainID, 0, height, 0, 2, blockID, defaultVoteTime) 381 vote2 = makeVote(t, val, chainID, 0, height, 1, 2, blockID2, defaultVoteTime.Add(1*time.Second)) 382 vote3 = makeVote(t, val, chainID, 0, height, 2, 2, blockID, defaultVoteTime) 383 ) 384 385 ev := NewPotentialAmnesiaEvidence(vote1, vote2) 386 387 assert.Equal(t, height, ev.Height()) 388 assert.Equal(t, vote2.Timestamp, ev.Time()) 389 assert.EqualValues(t, vote1.ValidatorAddress, ev.Address()) 390 assert.NotEmpty(t, ev.Hash()) 391 assert.NotEmpty(t, ev.Bytes()) 392 pubKey, err := val.GetPubKey() 393 require.NoError(t, err) 394 assert.NoError(t, ev.Verify(chainID, pubKey)) 395 assert.Error(t, ev.Verify("other", pubKey)) 396 privKey2 := ed25519.GenPrivKey() 397 pubKey2 := privKey2.PubKey() 398 assert.Error(t, ev.Verify("other", pubKey2)) 399 assert.True(t, ev.Equal(ev)) 400 assert.NoError(t, ev.ValidateBasic()) 401 assert.NotEmpty(t, ev.String()) 402 403 ev2 := &PotentialAmnesiaEvidence{ 404 VoteA: vote1, 405 VoteB: vote2, 406 HeightStamp: 5, 407 } 408 409 assert.True(t, ev.Equal(ev2)) 410 assert.Equal(t, ev.Hash(), ev2.Hash()) 411 412 ev3 := &PotentialAmnesiaEvidence{ 413 VoteA: vote2, 414 VoteB: vote1, 415 } 416 417 assert.Error(t, ev3.ValidateBasic()) 418 419 ev3 = NewPotentialAmnesiaEvidence(vote2, vote1) 420 assert.True(t, ev3.Equal(ev)) 421 422 ev4 := &PotentialAmnesiaEvidence{ 423 VoteA: vote3, 424 VoteB: vote2, 425 } 426 427 assert.NoError(t, ev4.ValidateBasic()) 428 assert.NotEqual(t, ev.Hash(), ev4.Hash()) 429 assert.False(t, ev.Equal(ev4)) 430 431 } 432 433 func TestProofOfLockChange(t *testing.T) { 434 const ( 435 chainID = "test_chain_id" 436 height int64 = 37 437 ) 438 // 1: valid POLC - nothing should fail 439 voteSet, valSet, privValidators, blockID := buildVoteSet(height, 1, 3, 7, 0, tmproto.PrecommitType) 440 pubKey, err := privValidators[7].GetPubKey() 441 require.NoError(t, err) 442 polc, err := NewPOLCFromVoteSet(voteSet, pubKey, blockID) 443 assert.NoError(t, err) 444 445 assert.Equal(t, height, polc.Height()) 446 assert.NoError(t, polc.ValidateBasic()) 447 assert.NoError(t, polc.ValidateVotes(valSet, chainID)) 448 assert.NotEmpty(t, polc.String()) 449 450 // tamper with one of the votes 451 polc.Votes[0].Timestamp = time.Now().Add(1 * time.Second) 452 err = polc.ValidateVotes(valSet, chainID) 453 t.Log(err) 454 assert.Error(t, err) 455 456 // remove a vote such that majority wasn't reached 457 polc.Votes = polc.Votes[1:] 458 err = polc.ValidateVotes(valSet, chainID) 459 t.Log(err) 460 assert.Error(t, err) 461 462 // test validate basic on a set of bad cases 463 var badPOLCs []*ProofOfLockChange 464 // 2: node has already voted in next round 465 pubKey, err = privValidators[0].GetPubKey() 466 require.NoError(t, err) 467 polc2 := newPOLCFromVoteSet(voteSet, pubKey, blockID) 468 badPOLCs = append(badPOLCs, polc2) 469 // 3: one vote was from a different round 470 voteSet, _, privValidators, blockID = buildVoteSet(height, 1, 3, 7, 0, tmproto.PrecommitType) 471 pubKey, err = privValidators[7].GetPubKey() 472 require.NoError(t, err) 473 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 474 badVote := makeVote(t, privValidators[8], chainID, 8, height, 2, 2, blockID, defaultVoteTime) 475 polc.Votes = append(polc.Votes, badVote) 476 badPOLCs = append(badPOLCs, polc) 477 // 4: one vote was from a different height 478 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 479 badVote = makeVote(t, privValidators[8], chainID, 8, height+1, 1, 2, blockID, defaultVoteTime) 480 polc.Votes = append(polc.Votes, badVote) 481 badPOLCs = append(badPOLCs, polc) 482 // 5: one vote was from a different vote type 483 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 484 badVote = makeVote(t, privValidators[8], chainID, 8, height, 1, 1, blockID, defaultVoteTime) 485 polc.Votes = append(polc.Votes, badVote) 486 badPOLCs = append(badPOLCs, polc) 487 // 5: one of the votes was for a nil block 488 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 489 badVote = makeVote(t, privValidators[8], chainID, 8, height, 1, 2, BlockID{}, defaultVoteTime) 490 polc.Votes = append(polc.Votes, badVote) 491 badPOLCs = append(badPOLCs, polc) 492 493 for idx, polc := range badPOLCs { 494 err := polc.ValidateBasic() 495 t.Logf("case: %d: %v", idx+2, err) 496 assert.Error(t, err) 497 if err == nil { 498 t.Errorf("test no. %d failed", idx+2) 499 } 500 } 501 502 } 503 504 func TestAmnesiaEvidence(t *testing.T) { 505 const ( 506 chainID = "test_chain_id" 507 height int64 = 37 508 ) 509 510 voteSet, valSet, privValidators, blockID := buildVoteSet(height, 1, 2, 7, 0, tmproto.PrecommitType) 511 512 var ( 513 val = privValidators[7] 514 pubKey, _ = val.GetPubKey() 515 blockID2 = makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 516 vote1 = makeVote(t, val, chainID, 7, height, 0, 2, blockID2, time.Now()) 517 vote2 = makeVote(t, val, chainID, 7, height, 1, 2, blockID, 518 time.Now().Add(time.Second)) 519 vote3 = makeVote(t, val, chainID, 7, height, 2, 2, blockID2, time.Now()) 520 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 521 ) 522 523 require.False(t, polc.IsAbsent()) 524 525 pe := &PotentialAmnesiaEvidence{ 526 VoteA: vote1, 527 VoteB: vote2, 528 } 529 530 emptyAmnesiaEvidence := NewAmnesiaEvidence(pe, NewEmptyPOLC()) 531 532 assert.NoError(t, emptyAmnesiaEvidence.ValidateBasic()) 533 violated, reason := emptyAmnesiaEvidence.ViolatedConsensus() 534 if assert.True(t, violated) { 535 assert.Equal(t, reason, "no proof of lock was provided") 536 } 537 assert.NoError(t, emptyAmnesiaEvidence.Verify(chainID, pubKey)) 538 539 completeAmnesiaEvidence := NewAmnesiaEvidence(pe, polc) 540 541 assert.NoError(t, completeAmnesiaEvidence.ValidateBasic()) 542 violated, reason = completeAmnesiaEvidence.ViolatedConsensus() 543 if !assert.False(t, violated) { 544 t.Log(reason) 545 } 546 assert.NoError(t, completeAmnesiaEvidence.Verify(chainID, pubKey)) 547 assert.NoError(t, completeAmnesiaEvidence.Polc.ValidateVotes(valSet, chainID)) 548 549 assert.True(t, completeAmnesiaEvidence.Equal(emptyAmnesiaEvidence)) 550 assert.Equal(t, completeAmnesiaEvidence.Hash(), emptyAmnesiaEvidence.Hash()) 551 assert.NotEmpty(t, completeAmnesiaEvidence.Hash()) 552 assert.NotEmpty(t, completeAmnesiaEvidence.Bytes()) 553 554 pe2 := &PotentialAmnesiaEvidence{ 555 VoteA: vote3, 556 VoteB: vote2, 557 } 558 559 // validator has incorrectly voted for a previous round after voting for a later round 560 ae := NewAmnesiaEvidence(pe2, NewEmptyPOLC()) 561 assert.NoError(t, ae.ValidateBasic()) 562 violated, reason = ae.ViolatedConsensus() 563 if assert.True(t, violated) { 564 assert.Equal(t, reason, "validator went back and voted on a previous round") 565 } 566 567 var badAE []*AmnesiaEvidence 568 // 1) Polc is at an incorrect height 569 voteSet, _, _ = buildVoteSetForBlock(height+1, 1, 2, 7, 0, tmproto.PrecommitType, blockID) 570 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 571 badAE = append(badAE, NewAmnesiaEvidence(pe, polc)) 572 // 2) Polc is of a later round 573 voteSet, _, _ = buildVoteSetForBlock(height, 2, 2, 7, 0, tmproto.PrecommitType, blockID) 574 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 575 badAE = append(badAE, NewAmnesiaEvidence(pe, polc)) 576 // 3) Polc has a different public key 577 voteSet, _, privValidators = buildVoteSetForBlock(height, 1, 2, 7, 0, tmproto.PrecommitType, blockID) 578 pubKey2, _ := privValidators[7].GetPubKey() 579 polc = newPOLCFromVoteSet(voteSet, pubKey2, blockID) 580 badAE = append(badAE, NewAmnesiaEvidence(pe, polc)) 581 // 4) Polc has a different block ID 582 voteSet, _, _, blockID = buildVoteSet(height, 1, 2, 7, 0, tmproto.PrecommitType) 583 polc = newPOLCFromVoteSet(voteSet, pubKey, blockID) 584 badAE = append(badAE, NewAmnesiaEvidence(pe, polc)) 585 586 for idx, ae := range badAE { 587 t.Log(ae.ValidateBasic()) 588 if !assert.Error(t, ae.ValidateBasic()) { 589 t.Errorf("test no. %d failed", idx+1) 590 } 591 } 592 593 } 594 595 func makeVote( 596 t *testing.T, val PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID BlockID, 597 time time.Time) *Vote { 598 pubKey, err := val.GetPubKey() 599 require.NoError(t, err) 600 v := &Vote{ 601 ValidatorAddress: pubKey.Address(), 602 ValidatorIndex: valIndex, 603 Height: height, 604 Round: round, 605 Type: tmproto.SignedMsgType(step), 606 BlockID: blockID, 607 Timestamp: time, 608 } 609 610 vpb := v.ToProto() 611 err = val.SignVote(chainID, vpb) 612 if err != nil { 613 panic(err) 614 } 615 v.Signature = vpb.Signature 616 return v 617 } 618 619 func makeHeaderRandom() *Header { 620 return &Header{ 621 ChainID: tmrand.Str(12), 622 Height: int64(tmrand.Uint16()) + 1, 623 Time: time.Now(), 624 LastBlockID: makeBlockIDRandom(), 625 LastCommitHash: crypto.CRandBytes(tmhash.Size), 626 DataHash: crypto.CRandBytes(tmhash.Size), 627 ValidatorsHash: crypto.CRandBytes(tmhash.Size), 628 NextValidatorsHash: crypto.CRandBytes(tmhash.Size), 629 ConsensusHash: crypto.CRandBytes(tmhash.Size), 630 AppHash: crypto.CRandBytes(tmhash.Size), 631 LastResultsHash: crypto.CRandBytes(tmhash.Size), 632 EvidenceHash: crypto.CRandBytes(tmhash.Size), 633 ProposerAddress: crypto.CRandBytes(crypto.AddressSize), 634 } 635 } 636 637 func TestEvidenceProto(t *testing.T) { 638 // -------- Votes -------- 639 val := NewMockPV() 640 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 641 blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 642 const chainID = "mychain" 643 v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime) 644 v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, defaultVoteTime) 645 646 // -------- SignedHeaders -------- 647 const height int64 = 37 648 649 var ( 650 header1 = makeHeaderRandom() 651 header2 = makeHeaderRandom() 652 ) 653 654 header1.Height = height 655 header1.LastBlockID = blockID 656 header1.ChainID = chainID 657 658 header2.Height = height 659 header2.LastBlockID = blockID 660 header2.ChainID = chainID 661 662 voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) 663 voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet) 664 665 commit1, err := MakeCommit(BlockID{ 666 Hash: header1.Hash(), 667 PartSetHeader: PartSetHeader{ 668 Total: 100, 669 Hash: crypto.CRandBytes(tmhash.Size), 670 }, 671 }, height, 1, voteSet1, vals, time.Now()) 672 require.NoError(t, err) 673 commit2, err := MakeCommit(BlockID{ 674 Hash: header2.Hash(), 675 PartSetHeader: PartSetHeader{ 676 Total: 100, 677 Hash: crypto.CRandBytes(tmhash.Size), 678 }, 679 }, height, 1, voteSet2, vals, time.Now()) 680 require.NoError(t, err) 681 682 h1 := &SignedHeader{ 683 Header: header1, 684 Commit: commit1, 685 } 686 h2 := &SignedHeader{ 687 Header: header2, 688 Commit: commit2, 689 } 690 691 tests := []struct { 692 testName string 693 evidence Evidence 694 toProtoErr bool 695 fromProtoErr bool 696 }{ 697 {"nil fail", nil, true, true}, 698 {"DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, false, true}, 699 {"DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true}, 700 {"DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true}, 701 {"DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false}, 702 {"ConflictingHeadersEvidence empty fail", &ConflictingHeadersEvidence{}, false, true}, 703 {"ConflictingHeadersEvidence nil H2", &ConflictingHeadersEvidence{H1: h1, H2: nil}, false, true}, 704 {"ConflictingHeadersEvidence nil H1", &ConflictingHeadersEvidence{H1: nil, H2: h2}, false, true}, 705 {"ConflictingHeadersEvidence success", &ConflictingHeadersEvidence{H1: h1, H2: h2}, false, false}, 706 {"LunaticValidatorEvidence success", &LunaticValidatorEvidence{Header: header1, 707 Vote: v, InvalidHeaderField: "ValidatorsHash"}, false, true}, 708 {"&LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true}, 709 {"LunaticValidatorEvidence only header fail", &LunaticValidatorEvidence{Header: header1}, false, true}, 710 {"LunaticValidatorEvidence only vote fail", &LunaticValidatorEvidence{Vote: v}, false, true}, 711 {"LunaticValidatorEvidence header & vote fail", &LunaticValidatorEvidence{Header: header1, Vote: v}, false, true}, 712 {"LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true}, 713 {"PotentialAmnesiaEvidence empty fail", &PotentialAmnesiaEvidence{}, false, true}, 714 {"PotentialAmnesiaEvidence nil VoteB", &PotentialAmnesiaEvidence{VoteA: v, VoteB: nil}, false, true}, 715 {"PotentialAmnesiaEvidence nil VoteA", &PotentialAmnesiaEvidence{VoteA: nil, VoteB: v2}, false, true}, 716 {"PotentialAmnesiaEvidence success", &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v}, false, false}, 717 {"PhantomValidatorEvidence empty fail", &PhantomValidatorEvidence{}, false, true}, 718 {"PhantomValidatorEvidence nil LastHeightValidatorWasInSet", &PhantomValidatorEvidence{Vote: v}, false, true}, 719 {"PhantomValidatorEvidence nil Vote", &PhantomValidatorEvidence{LastHeightValidatorWasInSet: 2}, false, true}, 720 {"PhantomValidatorEvidence success", &PhantomValidatorEvidence{Vote: v2, LastHeightValidatorWasInSet: 2}, 721 false, false}, 722 {"AmnesiaEvidence nil ProofOfLockChange", &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{}, 723 Polc: NewEmptyPOLC()}, false, true}, 724 {"AmnesiaEvidence nil Polc", 725 &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v}, 726 Polc: &ProofOfLockChange{}}, false, false}, 727 {"AmnesiaEvidence success", &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v}, 728 Polc: NewEmptyPOLC()}, false, false}, 729 } 730 for _, tt := range tests { 731 tt := tt 732 t.Run(tt.testName, func(t *testing.T) { 733 pb, err := EvidenceToProto(tt.evidence) 734 if tt.toProtoErr { 735 assert.Error(t, err, tt.testName) 736 return 737 } 738 assert.NoError(t, err, tt.testName) 739 740 evi, err := EvidenceFromProto(pb) 741 if tt.fromProtoErr { 742 assert.Error(t, err, tt.testName) 743 return 744 } 745 require.Equal(t, tt.evidence, evi, tt.testName) 746 }) 747 } 748 } 749 750 func TestProofOfLockChangeProtoBuf(t *testing.T) { 751 // -------- Votes -------- 752 val := NewMockPV() 753 val2 := NewMockPV() 754 val3 := NewMockPV() 755 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 756 const chainID = "mychain" 757 v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime) 758 v2 := makeVote(t, val2, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime) 759 760 testCases := []struct { 761 msg string 762 polc *ProofOfLockChange 763 toProtoErr bool 764 fromProtoErr bool 765 }{ 766 {"failure, empty key", &ProofOfLockChange{Votes: []*Vote{v, v2}, PubKey: nil}, true, false}, 767 {"failure, empty votes", &ProofOfLockChange{PubKey: val3.PrivKey.PubKey()}, true, false}, 768 {"success empty ProofOfLockChange", NewEmptyPOLC(), false, false}, 769 {"success", &ProofOfLockChange{Votes: []*Vote{v, v2}, PubKey: val3.PrivKey.PubKey()}, false, false}, 770 } 771 for _, tc := range testCases { 772 tc := tc 773 pbpolc, err := tc.polc.ToProto() 774 if tc.toProtoErr { 775 assert.Error(t, err, tc.msg) 776 } else { 777 assert.NoError(t, err, tc.msg) 778 } 779 780 c, err := ProofOfLockChangeFromProto(pbpolc) 781 if !tc.fromProtoErr { 782 assert.NoError(t, err, tc.msg) 783 if !tc.toProtoErr { 784 assert.Equal(t, tc.polc, c, tc.msg) 785 } 786 } else { 787 assert.Error(t, err, tc.msg) 788 } 789 } 790 }