github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/evidence/pool_test.go (about) 1 package evidence 2 3 import ( 4 "os" 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 dbm "github.com/tendermint/tm-db" 13 14 "github.com/tendermint/tendermint/crypto/tmhash" 15 "github.com/tendermint/tendermint/libs/bytes" 16 "github.com/tendermint/tendermint/libs/log" 17 tmrand "github.com/tendermint/tendermint/libs/rand" 18 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 19 sm "github.com/tendermint/tendermint/state" 20 "github.com/tendermint/tendermint/store" 21 "github.com/tendermint/tendermint/types" 22 tmtime "github.com/tendermint/tendermint/types/time" 23 ) 24 25 func TestMain(m *testing.M) { 26 27 code := m.Run() 28 os.Exit(code) 29 } 30 31 const evidenceChainID = "test_chain" 32 33 func TestEvidencePool(t *testing.T) { 34 var ( 35 val = types.NewMockPV() 36 valAddr = val.PrivKey.PubKey().Address() 37 height = int64(52) 38 stateDB = initializeValidatorState(val, height) 39 evidenceDB = dbm.NewMemDB() 40 blockStoreDB = dbm.NewMemDB() 41 blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr) 42 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 43 44 goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID) 45 badEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(1, evidenceTime, val, evidenceChainID) 46 ) 47 48 pool, err := NewPool(stateDB, evidenceDB, blockStore) 49 require.NoError(t, err) 50 51 // bad evidence 52 err = pool.AddEvidence(badEvidence) 53 if assert.Error(t, err) { 54 assert.Contains(t, err.Error(), "is too old; min height is 32 and evidence can not be older than") 55 } 56 assert.False(t, pool.IsPending(badEvidence)) 57 assert.True(t, pool.IsEvidenceExpired(badEvidence)) 58 59 // good evidence 60 evAdded := make(chan struct{}) 61 go func() { 62 <-pool.EvidenceWaitChan() 63 close(evAdded) 64 }() 65 66 err = pool.AddEvidence(goodEvidence) 67 require.NoError(t, err) 68 69 select { 70 case <-evAdded: 71 case <-time.After(5 * time.Second): 72 t.Fatal("evidence was not added to list after 5s") 73 } 74 75 assert.Equal(t, 1, pool.evidenceList.Len()) 76 77 // if we send it again, it shouldnt add and return an error 78 err = pool.AddEvidence(goodEvidence) 79 assert.NoError(t, err) 80 assert.Equal(t, 1, pool.evidenceList.Len()) 81 } 82 83 func TestProposingAndCommittingEvidence(t *testing.T) { 84 var ( 85 val = types.NewMockPV() 86 height = int64(1) 87 stateDB = initializeValidatorState(val, height) 88 evidenceDB = dbm.NewMemDB() 89 blockStoreDB = dbm.NewMemDB() 90 blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), val.PrivKey.PubKey().Address()) 91 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 92 ) 93 94 pool, err := NewPool(stateDB, evidenceDB, blockStore) 95 require.NoError(t, err) 96 97 // evidence not seen yet: 98 evidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID) 99 assert.False(t, pool.IsCommitted(evidence)) 100 101 // evidence seen but not yet committed: 102 assert.NoError(t, pool.AddEvidence(evidence)) 103 assert.False(t, pool.IsCommitted(evidence)) 104 105 // test evidence is proposed 106 proposedEvidence := pool.AllPendingEvidence() 107 assert.Equal(t, proposedEvidence[0], evidence) 108 109 // evidence seen and committed: 110 pool.MarkEvidenceAsCommitted(height, proposedEvidence) 111 assert.True(t, pool.IsCommitted(evidence)) 112 assert.False(t, pool.IsPending(evidence)) 113 assert.Equal(t, 0, pool.evidenceList.Len()) 114 115 // evidence should 116 } 117 118 func TestAddEvidence(t *testing.T) { 119 var ( 120 val = types.NewMockPV() 121 valAddr = val.PrivKey.PubKey().Address() 122 height = int64(30) 123 stateDB = initializeValidatorState(val, height) 124 evidenceDB = dbm.NewMemDB() 125 blockStoreDB = dbm.NewMemDB() 126 blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr) 127 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 128 ) 129 130 pool, err := NewPool(stateDB, evidenceDB, blockStore) 131 require.NoError(t, err) 132 133 testCases := []struct { 134 evHeight int64 135 evTime time.Time 136 expErr bool 137 evDescription string 138 }{ 139 {height, time.Now(), false, "valid evidence"}, 140 {height, evidenceTime, false, "valid evidence (despite old time)"}, 141 {int64(1), time.Now(), false, "valid evidence (despite old height)"}, 142 {int64(1), evidenceTime, true, 143 "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"}, 144 } 145 146 for _, tc := range testCases { 147 tc := tc 148 t.Run(tc.evDescription, func(t *testing.T) { 149 ev := types.NewMockDuplicateVoteEvidence(tc.evHeight, tc.evTime, evidenceChainID) 150 err := pool.AddEvidence(ev) 151 if tc.expErr { 152 assert.Error(t, err) 153 t.Log(err) 154 } 155 }) 156 } 157 } 158 159 func TestEvidencePoolUpdate(t *testing.T) { 160 var ( 161 val = types.NewMockPV() 162 valAddr = val.PrivKey.PubKey().Address() 163 height = int64(21) 164 stateDB = initializeValidatorState(val, height) 165 evidenceDB = dbm.NewMemDB() 166 blockStoreDB = dbm.NewMemDB() 167 state = sm.LoadState(stateDB) 168 blockStore = initializeBlockStore(blockStoreDB, state, valAddr) 169 ) 170 171 pool, err := NewPool(stateDB, evidenceDB, blockStore) 172 require.NoError(t, err) 173 174 // create new block (no need to save it to blockStore) 175 evidence := types.NewMockDuplicateVoteEvidence(height, time.Now(), evidenceChainID) 176 lastCommit := makeCommit(height, valAddr) 177 block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence}) 178 // update state (partially) 179 state.LastBlockHeight = height + 1 180 181 pool.Update(block, state) 182 183 // a) Update marks evidence as committed 184 assert.True(t, pool.IsCommitted(evidence)) 185 // b) Update updates valToLastHeight map 186 assert.Equal(t, height+1, pool.ValidatorLastHeight(valAddr)) 187 } 188 189 func TestEvidencePoolNewPool(t *testing.T) { 190 var ( 191 val = types.NewMockPV() 192 valAddr = val.PrivKey.PubKey().Address() 193 height = int64(1) 194 stateDB = initializeValidatorState(val, height) 195 evidenceDB = dbm.NewMemDB() 196 blockStoreDB = dbm.NewMemDB() 197 state = sm.LoadState(stateDB) 198 blockStore = initializeBlockStore(blockStoreDB, state, valAddr) 199 ) 200 201 pool, err := NewPool(stateDB, evidenceDB, blockStore) 202 require.NoError(t, err) 203 204 assert.Equal(t, height, pool.ValidatorLastHeight(valAddr)) 205 assert.EqualValues(t, 0, pool.ValidatorLastHeight([]byte("non-existent-validator"))) 206 } 207 208 func TestAddingAndPruningPOLC(t *testing.T) { 209 var ( 210 val = types.NewMockPV() 211 valAddr = val.PrivKey.PubKey().Address() 212 stateDB = initializeValidatorState(val, 1) 213 evidenceDB = dbm.NewMemDB() 214 blockStoreDB = dbm.NewMemDB() 215 state = sm.LoadState(stateDB) 216 blockStore = initializeBlockStore(blockStoreDB, state, valAddr) 217 height = state.ConsensusParams.Evidence.MaxAgeNumBlocks * 2 218 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 219 firstBlockID = types.BlockID{ 220 Hash: tmrand.Bytes(tmhash.Size), 221 PartSetHeader: types.PartSetHeader{ 222 Total: 1, 223 Hash: tmrand.Bytes(tmhash.Size), 224 }, 225 } 226 ) 227 228 voteA := makeVote(1, 1, 0, val.PrivKey.PubKey().Address(), firstBlockID, evidenceTime) 229 vA := voteA.ToProto() 230 err := val.SignVote(evidenceChainID, vA) 231 require.NoError(t, err) 232 voteA.Signature = vA.Signature 233 234 pubKey, _ := types.NewMockPV().GetPubKey() 235 polc := &types.ProofOfLockChange{ 236 Votes: []*types.Vote{voteA}, 237 PubKey: pubKey, 238 } 239 240 pool, err := NewPool(stateDB, evidenceDB, blockStore) 241 require.NoError(t, err) 242 243 err = pool.AddPOLC(polc) 244 assert.NoError(t, err) 245 246 // should be able to retrieve polc 247 newPolc, err := pool.RetrievePOLC(1, 1) 248 assert.NoError(t, err) 249 assert.True(t, polc.Equal(newPolc)) 250 251 // should not be able to retrieve because it doesn't exist 252 emptyPolc, err := pool.RetrievePOLC(2, 1) 253 assert.NoError(t, err) 254 assert.Nil(t, emptyPolc) 255 256 lastCommit := makeCommit(height-1, valAddr) 257 block := types.MakeBlock(height, []types.Tx{}, lastCommit, []types.Evidence{}) 258 // update state (partially) 259 state.LastBlockHeight = height 260 pool.state.LastBlockHeight = height 261 262 // update should prune the polc 263 pool.Update(block, state) 264 265 emptyPolc, err = pool.RetrievePOLC(1, 1) 266 assert.NoError(t, err) 267 assert.Nil(t, emptyPolc) 268 269 } 270 271 func TestRecoverPendingEvidence(t *testing.T) { 272 var ( 273 val = types.NewMockPV() 274 valAddr = val.PrivKey.PubKey().Address() 275 height = int64(30) 276 stateDB = initializeValidatorState(val, height) 277 evidenceDB = dbm.NewMemDB() 278 blockStoreDB = dbm.NewMemDB() 279 state = sm.LoadState(stateDB) 280 blockStore = initializeBlockStore(blockStoreDB, state, valAddr) 281 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 282 goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), val, evidenceChainID) 283 expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), evidenceTime, val, evidenceChainID) 284 ) 285 286 // load good evidence 287 goodKey := keyPending(goodEvidence) 288 evi, err := types.EvidenceToProto(goodEvidence) 289 require.NoError(t, err) 290 goodEvidenceBytes, err := proto.Marshal(evi) 291 require.NoError(t, err) 292 _ = evidenceDB.Set(goodKey, goodEvidenceBytes) 293 294 // load expired evidence 295 expiredKey := keyPending(expiredEvidence) 296 eevi, err := types.EvidenceToProto(expiredEvidence) 297 require.NoError(t, err) 298 299 expiredEvidenceBytes, err := proto.Marshal(eevi) 300 require.NoError(t, err) 301 302 _ = evidenceDB.Set(expiredKey, expiredEvidenceBytes) 303 pool, err := NewPool(stateDB, evidenceDB, blockStore) 304 require.NoError(t, err) 305 assert.Equal(t, 1, pool.evidenceList.Len()) 306 assert.True(t, pool.IsPending(goodEvidence)) 307 } 308 309 // Comprehensive set of test cases relating to the adding, upgrading and overall 310 // processing of PotentialAmnesiaEvidence and AmnesiaEvidence 311 func TestAddingPotentialAmnesiaEvidence(t *testing.T) { 312 var ( 313 val = types.NewMockPV() 314 val2 = types.NewMockPV() 315 pubKey = val.PrivKey.PubKey() 316 pubKey2 = val2.PrivKey.PubKey() 317 valSet = &types.ValidatorSet{ 318 Validators: []*types.Validator{ 319 val.ExtractIntoValidator(1), 320 val2.ExtractIntoValidator(3), 321 }, 322 Proposer: val.ExtractIntoValidator(1), 323 } 324 height = int64(30) 325 stateDB = initializeStateFromValidatorSet(valSet, height) 326 evidenceDB = dbm.NewMemDB() 327 blockStoreDB = dbm.NewMemDB() 328 state = sm.LoadState(stateDB) 329 blockStore = initializeBlockStore(blockStoreDB, state, pubKey.Address()) 330 //evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 331 firstBlockID = types.BlockID{ 332 Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 333 PartSetHeader: types.PartSetHeader{ 334 Total: 1, 335 Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 336 }, 337 } 338 secondBlockID = types.BlockID{ 339 Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), 340 PartSetHeader: types.PartSetHeader{ 341 Total: 1, 342 Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), 343 }, 344 } 345 evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 346 ) 347 348 // TEST SETUP 349 pool, err := NewPool(stateDB, evidenceDB, blockStore) 350 require.NoError(t, err) 351 352 pool.SetLogger(log.TestingLogger()) 353 354 voteA := makeVote(height, 0, 0, pubKey.Address(), firstBlockID, evidenceTime) 355 vA := voteA.ToProto() 356 err = val.SignVote(evidenceChainID, vA) 357 voteA.Signature = vA.Signature 358 require.NoError(t, err) 359 voteB := makeVote(height, 1, 0, pubKey.Address(), secondBlockID, evidenceTime.Add(3*time.Second)) 360 vB := voteB.ToProto() 361 err = val.SignVote(evidenceChainID, vB) 362 voteB.Signature = vB.Signature 363 require.NoError(t, err) 364 voteC := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(2*time.Second)) 365 vC := voteC.ToProto() 366 err = val.SignVote(evidenceChainID, vC) 367 voteC.Signature = vC.Signature 368 require.NoError(t, err) 369 ev := &types.PotentialAmnesiaEvidence{ 370 VoteA: voteA, 371 VoteB: voteB, 372 } 373 374 polc := &types.ProofOfLockChange{ 375 Votes: []*types.Vote{voteB}, 376 PubKey: pubKey2, 377 } 378 err = pool.AddPOLC(polc) 379 require.NoError(t, err) 380 381 polc, err = pool.RetrievePOLC(height, 1) 382 require.NoError(t, err) 383 require.NotEmpty(t, polc) 384 385 secondValVote := makeVote(height, 1, 0, pubKey2.Address(), secondBlockID, evidenceTime.Add(1*time.Second)) 386 vv2 := secondValVote.ToProto() 387 err = val2.SignVote(evidenceChainID, vv2) 388 require.NoError(t, err) 389 secondValVote.Signature = vv2.Signature 390 391 validPolc := &types.ProofOfLockChange{ 392 Votes: []*types.Vote{secondValVote}, 393 PubKey: pubKey, 394 } 395 396 // CASE A 397 pool.logger.Info("CASE A") 398 // we expect the evidence pool to find the polc but log an error as the polc is not valid -> vote was 399 // not from a validator in this set. However, an error isn't thrown because the evidence pool 400 // should still be able to save the regular potential amnesia evidence. 401 err = pool.AddEvidence(ev) 402 assert.NoError(t, err) 403 404 // evidence requires trial period until it is available -> we expect no evidence to be returned 405 assert.Equal(t, 0, len(pool.PendingEvidence(1))) 406 assert.True(t, pool.IsOnTrial(ev)) 407 408 nextHeight := pool.nextEvidenceTrialEndedHeight 409 assert.Greater(t, nextHeight, int64(0)) 410 411 // CASE B 412 pool.logger.Info("CASE B") 413 // evidence is not ready to be upgraded so we return the height we expect the evidence to be. 414 nextHeight = pool.upgradePotentialAmnesiaEvidence() 415 assert.Equal(t, height+pool.state.ConsensusParams.Evidence.ProofTrialPeriod, nextHeight) 416 417 // CASE C 418 pool.logger.Info("CASE C") 419 // now evidence is ready to be upgraded to amnesia evidence -> we expect -1 to be the next height as their is 420 // no more pending potential amnesia evidence left 421 lastCommit := makeCommit(height+1, pubKey.Address()) 422 block := types.MakeBlock(height+2, []types.Tx{}, lastCommit, []types.Evidence{}) 423 state.LastBlockHeight = height + 2 424 425 pool.Update(block, state) 426 assert.Equal(t, int64(-1), pool.nextEvidenceTrialEndedHeight) 427 428 assert.Equal(t, 1, len(pool.PendingEvidence(1))) 429 430 // CASE D 431 pool.logger.Info("CASE D") 432 // evidence of voting back in the past which is instantly punishable -> amnesia evidence is made directly 433 ev2 := &types.PotentialAmnesiaEvidence{ 434 VoteA: voteC, 435 VoteB: voteB, 436 } 437 err = pool.AddEvidence(ev2) 438 assert.NoError(t, err) 439 expectedAe := &types.AmnesiaEvidence{ 440 PotentialAmnesiaEvidence: ev2, 441 Polc: types.NewEmptyPOLC(), 442 } 443 444 assert.True(t, pool.IsPending(expectedAe)) 445 assert.Equal(t, 2, len(pool.AllPendingEvidence())) 446 447 // CASE E 448 pool.logger.Info("CASE E") 449 // test for receiving amnesia evidence 450 ae := types.NewAmnesiaEvidence(ev, types.NewEmptyPOLC()) 451 // we need to run the trial period ourselves so amnesia evidence should not be added, instead 452 // we should extract out the potential amnesia evidence and trying to add that before realising 453 // that we already have it -> no error 454 err = pool.AddEvidence(ae) 455 assert.NoError(t, err) 456 assert.Equal(t, 2, len(pool.AllPendingEvidence())) 457 458 voteD := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(4*time.Second)) 459 vD := voteD.ToProto() 460 err = val.SignVote(evidenceChainID, vD) 461 require.NoError(t, err) 462 voteD.Signature = vD.Signature 463 464 // CASE F 465 pool.logger.Info("CASE F") 466 // a new amnesia evidence is seen. It has an empty polc so we should extract the potential amnesia evidence 467 // and start our own trial 468 newPe := &types.PotentialAmnesiaEvidence{ 469 VoteA: voteB, 470 VoteB: voteD, 471 } 472 newAe := &types.AmnesiaEvidence{ 473 PotentialAmnesiaEvidence: newPe, 474 Polc: types.NewEmptyPOLC(), 475 } 476 err = pool.AddEvidence(newAe) 477 assert.NoError(t, err) 478 assert.Equal(t, 2, len(pool.AllPendingEvidence())) 479 assert.True(t, pool.IsOnTrial(newPe)) 480 481 // CASE G 482 pool.logger.Info("CASE G") 483 // Finally, we receive an amnesia evidence containing a valid polc for an earlier potential amnesia evidence 484 // that we have already upgraded to. We should ad this new amnesia evidence in replace of the prior 485 // amnesia evidence with an empty polc that we have 486 aeWithPolc := &types.AmnesiaEvidence{ 487 PotentialAmnesiaEvidence: ev, 488 Polc: validPolc, 489 } 490 err = pool.AddEvidence(aeWithPolc) 491 assert.NoError(t, err) 492 assert.True(t, pool.IsPending(aeWithPolc)) 493 assert.Equal(t, 2, len(pool.AllPendingEvidence())) 494 t.Log(pool.AllPendingEvidence()) 495 496 } 497 498 func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) dbm.DB { 499 stateDB := dbm.NewMemDB() 500 state := sm.State{ 501 ChainID: evidenceChainID, 502 LastBlockHeight: height, 503 LastBlockTime: tmtime.Now(), 504 Validators: valSet, 505 NextValidators: valSet.CopyIncrementProposerPriority(1), 506 LastValidators: valSet, 507 LastHeightValidatorsChanged: 1, 508 ConsensusParams: tmproto.ConsensusParams{ 509 Block: tmproto.BlockParams{ 510 MaxBytes: 22020096, 511 MaxGas: -1, 512 }, 513 Evidence: tmproto.EvidenceParams{ 514 MaxAgeNumBlocks: 20, 515 MaxAgeDuration: 48 * time.Hour, 516 MaxNum: 50, 517 ProofTrialPeriod: 1, 518 }, 519 }, 520 } 521 522 // save all states up to height 523 for i := int64(0); i <= height; i++ { 524 state.LastBlockHeight = i 525 sm.SaveState(stateDB, state) 526 } 527 528 return stateDB 529 } 530 531 func initializeValidatorState(privVal types.PrivValidator, height int64) dbm.DB { 532 533 pubKey, _ := privVal.GetPubKey() 534 validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey} 535 536 // create validator set and state 537 valSet := &types.ValidatorSet{ 538 Validators: []*types.Validator{validator}, 539 Proposer: validator, 540 } 541 542 return initializeStateFromValidatorSet(valSet, height) 543 } 544 545 // initializeBlockStore creates a block storage and populates it w/ a dummy 546 // block at +height+. 547 func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore { 548 blockStore := store.NewBlockStore(db) 549 550 for i := int64(1); i <= state.LastBlockHeight; i++ { 551 lastCommit := makeCommit(i-1, valAddr) 552 block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil, 553 state.Validators.GetProposer().Address) 554 555 const parts = 1 556 partSet := block.MakePartSet(parts) 557 558 seenCommit := makeCommit(i, valAddr) 559 blockStore.SaveBlock(block, partSet, seenCommit) 560 } 561 562 return blockStore 563 } 564 565 func makeCommit(height int64, valAddr []byte) *types.Commit { 566 commitSigs := []types.CommitSig{{ 567 BlockIDFlag: types.BlockIDFlagCommit, 568 ValidatorAddress: valAddr, 569 Timestamp: time.Now(), 570 Signature: []byte("Signature"), 571 }} 572 return types.NewCommit(height, 0, types.BlockID{}, commitSigs) 573 } 574 575 func makeVote(height int64, round, index int32, addr bytes.HexBytes, 576 blockID types.BlockID, time time.Time) *types.Vote { 577 return &types.Vote{ 578 Type: tmproto.SignedMsgType(2), 579 Height: height, 580 Round: round, 581 BlockID: blockID, 582 Timestamp: time, 583 ValidatorAddress: addr, 584 ValidatorIndex: index, 585 } 586 }