github.com/DFWallet/tendermint-cosmos@v0.0.2/consensus/common_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "sort" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/go-kit/kit/log/term" 16 "github.com/stretchr/testify/require" 17 18 "path" 19 20 dbm "github.com/tendermint/tm-db" 21 22 abcicli "github.com/DFWallet/tendermint-cosmos/abci/client" 23 "github.com/DFWallet/tendermint-cosmos/abci/example/counter" 24 "github.com/DFWallet/tendermint-cosmos/abci/example/kvstore" 25 abci "github.com/DFWallet/tendermint-cosmos/abci/types" 26 cfg "github.com/DFWallet/tendermint-cosmos/config" 27 cstypes "github.com/DFWallet/tendermint-cosmos/consensus/types" 28 tmbytes "github.com/DFWallet/tendermint-cosmos/libs/bytes" 29 "github.com/DFWallet/tendermint-cosmos/libs/log" 30 tmos "github.com/DFWallet/tendermint-cosmos/libs/os" 31 tmpubsub "github.com/DFWallet/tendermint-cosmos/libs/pubsub" 32 tmsync "github.com/DFWallet/tendermint-cosmos/libs/sync" 33 mempl "github.com/DFWallet/tendermint-cosmos/mempool" 34 "github.com/DFWallet/tendermint-cosmos/p2p" 35 "github.com/DFWallet/tendermint-cosmos/privval" 36 tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types" 37 sm "github.com/DFWallet/tendermint-cosmos/state" 38 "github.com/DFWallet/tendermint-cosmos/store" 39 "github.com/DFWallet/tendermint-cosmos/types" 40 tmtime "github.com/DFWallet/tendermint-cosmos/types/time" 41 ) 42 43 const ( 44 testSubscriber = "test-client" 45 ) 46 47 // A cleanupFunc cleans up any config / test files created for a particular 48 // test. 49 type cleanupFunc func() 50 51 // genesis, chain_id, priv_val 52 var ( 53 config *cfg.Config // NOTE: must be reset for each _test.go file 54 consensusReplayConfig *cfg.Config 55 ensureTimeout = time.Millisecond * 200 56 ) 57 58 func ensureDir(dir string, mode os.FileMode) { 59 if err := tmos.EnsureDir(dir, mode); err != nil { 60 panic(err) 61 } 62 } 63 64 func ResetConfig(name string) *cfg.Config { 65 return cfg.ResetTestRoot(name) 66 } 67 68 //------------------------------------------------------------------------------- 69 // validator stub (a kvstore consensus peer we control) 70 71 type validatorStub struct { 72 Index int32 // Validator index. NOTE: we don't assume validator set changes. 73 Height int64 74 Round int32 75 types.PrivValidator 76 VotingPower int64 77 lastVote *types.Vote 78 } 79 80 var testMinPower int64 = 10 81 82 func newValidatorStub(privValidator types.PrivValidator, valIndex int32) *validatorStub { 83 return &validatorStub{ 84 Index: valIndex, 85 PrivValidator: privValidator, 86 VotingPower: testMinPower, 87 } 88 } 89 90 func (vs *validatorStub) signVote( 91 voteType tmproto.SignedMsgType, 92 hash []byte, 93 header types.PartSetHeader) (*types.Vote, error) { 94 95 pubKey, err := vs.PrivValidator.GetPubKey() 96 if err != nil { 97 return nil, fmt.Errorf("can't get pubkey: %w", err) 98 } 99 100 vote := &types.Vote{ 101 ValidatorIndex: vs.Index, 102 ValidatorAddress: pubKey.Address(), 103 Height: vs.Height, 104 Round: vs.Round, 105 Timestamp: tmtime.Now(), 106 Type: voteType, 107 BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, 108 } 109 v := vote.ToProto() 110 if err := vs.PrivValidator.SignVote(config.ChainID(), v); err != nil { 111 return nil, fmt.Errorf("sign vote failed: %w", err) 112 } 113 114 // ref: signVote in FilePV, the vote should use the privious vote info when the sign data is the same. 115 if signDataIsEqual(vs.lastVote, v) { 116 v.Signature = vs.lastVote.Signature 117 v.Timestamp = vs.lastVote.Timestamp 118 } 119 120 vote.Signature = v.Signature 121 vote.Timestamp = v.Timestamp 122 123 return vote, err 124 } 125 126 // Sign vote for type/hash/header 127 func signVote(vs *validatorStub, voteType tmproto.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { 128 v, err := vs.signVote(voteType, hash, header) 129 if err != nil { 130 panic(fmt.Errorf("failed to sign vote: %v", err)) 131 } 132 133 vs.lastVote = v 134 135 return v 136 } 137 138 func signVotes( 139 voteType tmproto.SignedMsgType, 140 hash []byte, 141 header types.PartSetHeader, 142 vss ...*validatorStub) []*types.Vote { 143 votes := make([]*types.Vote, len(vss)) 144 for i, vs := range vss { 145 votes[i] = signVote(vs, voteType, hash, header) 146 } 147 return votes 148 } 149 150 func incrementHeight(vss ...*validatorStub) { 151 for _, vs := range vss { 152 vs.Height++ 153 } 154 } 155 156 func incrementRound(vss ...*validatorStub) { 157 for _, vs := range vss { 158 vs.Round++ 159 } 160 } 161 162 type ValidatorStubsByPower []*validatorStub 163 164 func (vss ValidatorStubsByPower) Len() int { 165 return len(vss) 166 } 167 168 func (vss ValidatorStubsByPower) Less(i, j int) bool { 169 vssi, err := vss[i].GetPubKey() 170 if err != nil { 171 panic(err) 172 } 173 vssj, err := vss[j].GetPubKey() 174 if err != nil { 175 panic(err) 176 } 177 178 if vss[i].VotingPower == vss[j].VotingPower { 179 return bytes.Compare(vssi.Address(), vssj.Address()) == -1 180 } 181 return vss[i].VotingPower > vss[j].VotingPower 182 } 183 184 func (vss ValidatorStubsByPower) Swap(i, j int) { 185 it := vss[i] 186 vss[i] = vss[j] 187 vss[i].Index = int32(i) 188 vss[j] = it 189 vss[j].Index = int32(j) 190 } 191 192 //------------------------------------------------------------------------------- 193 // Functions for transitioning the consensus state 194 195 func startTestRound(cs *State, height int64, round int32) { 196 cs.enterNewRound(height, round) 197 cs.startRoutines(0) 198 } 199 200 // Create proposal block from cs1 but sign it with vs. 201 func decideProposal( 202 cs1 *State, 203 vs *validatorStub, 204 height int64, 205 round int32, 206 ) (proposal *types.Proposal, block *types.Block) { 207 cs1.mtx.Lock() 208 block, blockParts := cs1.createProposalBlock() 209 validRound := cs1.ValidRound 210 chainID := cs1.state.ChainID 211 cs1.mtx.Unlock() 212 if block == nil { 213 panic("Failed to createProposalBlock. Did you forget to add commit for previous block?") 214 } 215 216 // Make proposal 217 polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} 218 proposal = types.NewProposal(height, round, polRound, propBlockID) 219 p := proposal.ToProto() 220 if err := vs.SignProposal(chainID, p); err != nil { 221 panic(err) 222 } 223 224 proposal.Signature = p.Signature 225 226 return 227 } 228 229 func addVotes(to *State, votes ...*types.Vote) { 230 for _, vote := range votes { 231 to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{vote}} 232 } 233 } 234 235 func signAddVotes( 236 to *State, 237 voteType tmproto.SignedMsgType, 238 hash []byte, 239 header types.PartSetHeader, 240 vss ...*validatorStub, 241 ) { 242 votes := signVotes(voteType, hash, header, vss...) 243 addVotes(to, votes...) 244 } 245 246 func validatePrevote(t *testing.T, cs *State, round int32, privVal *validatorStub, blockHash []byte) { 247 prevotes := cs.Votes.Prevotes(round) 248 pubKey, err := privVal.GetPubKey() 249 require.NoError(t, err) 250 address := pubKey.Address() 251 var vote *types.Vote 252 if vote = prevotes.GetByAddress(address); vote == nil { 253 panic("Failed to find prevote from validator") 254 } 255 if blockHash == nil { 256 if vote.BlockID.Hash != nil { 257 panic(fmt.Sprintf("Expected prevote to be for nil, got %X", vote.BlockID.Hash)) 258 } 259 } else { 260 if !bytes.Equal(vote.BlockID.Hash, blockHash) { 261 panic(fmt.Sprintf("Expected prevote to be for %X, got %X", blockHash, vote.BlockID.Hash)) 262 } 263 } 264 } 265 266 func validateLastPrecommit(t *testing.T, cs *State, privVal *validatorStub, blockHash []byte) { 267 votes := cs.LastCommit 268 pv, err := privVal.GetPubKey() 269 require.NoError(t, err) 270 address := pv.Address() 271 var vote *types.Vote 272 if vote = votes.GetByAddress(address); vote == nil { 273 panic("Failed to find precommit from validator") 274 } 275 if !bytes.Equal(vote.BlockID.Hash, blockHash) { 276 panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash)) 277 } 278 } 279 280 func validatePrecommit( 281 t *testing.T, 282 cs *State, 283 thisRound, 284 lockRound int32, 285 privVal *validatorStub, 286 votedBlockHash, 287 lockedBlockHash []byte, 288 ) { 289 precommits := cs.Votes.Precommits(thisRound) 290 pv, err := privVal.GetPubKey() 291 require.NoError(t, err) 292 address := pv.Address() 293 var vote *types.Vote 294 if vote = precommits.GetByAddress(address); vote == nil { 295 panic("Failed to find precommit from validator") 296 } 297 298 if votedBlockHash == nil { 299 if vote.BlockID.Hash != nil { 300 panic("Expected precommit to be for nil") 301 } 302 } else { 303 if !bytes.Equal(vote.BlockID.Hash, votedBlockHash) { 304 panic("Expected precommit to be for proposal block") 305 } 306 } 307 308 if lockedBlockHash == nil { 309 if cs.LockedRound != lockRound || cs.LockedBlock != nil { 310 panic(fmt.Sprintf( 311 "Expected to be locked on nil at round %d. Got locked at round %d with block %v", 312 lockRound, 313 cs.LockedRound, 314 cs.LockedBlock)) 315 } 316 } else { 317 if cs.LockedRound != lockRound || !bytes.Equal(cs.LockedBlock.Hash(), lockedBlockHash) { 318 panic(fmt.Sprintf( 319 "Expected block to be locked on round %d, got %d. Got locked block %X, expected %X", 320 lockRound, 321 cs.LockedRound, 322 cs.LockedBlock.Hash(), 323 lockedBlockHash)) 324 } 325 } 326 } 327 328 func validatePrevoteAndPrecommit( 329 t *testing.T, 330 cs *State, 331 thisRound, 332 lockRound int32, 333 privVal *validatorStub, 334 votedBlockHash, 335 lockedBlockHash []byte, 336 ) { 337 // verify the prevote 338 validatePrevote(t, cs, thisRound, privVal, votedBlockHash) 339 // verify precommit 340 cs.mtx.Lock() 341 validatePrecommit(t, cs, thisRound, lockRound, privVal, votedBlockHash, lockedBlockHash) 342 cs.mtx.Unlock() 343 } 344 345 func subscribeToVoter(cs *State, addr []byte) <-chan tmpubsub.Message { 346 votesSub, err := cs.eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, types.EventQueryVote) 347 if err != nil { 348 panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, types.EventQueryVote)) 349 } 350 ch := make(chan tmpubsub.Message) 351 go func() { 352 for msg := range votesSub.Out() { 353 vote := msg.Data().(types.EventDataVote) 354 // we only fire for our own votes 355 if bytes.Equal(addr, vote.Vote.ValidatorAddress) { 356 ch <- msg 357 } 358 } 359 }() 360 return ch 361 } 362 363 //------------------------------------------------------------------------------- 364 // consensus states 365 366 func newState(state sm.State, pv types.PrivValidator, app abci.Application) *State { 367 config := cfg.ResetTestRoot("consensus_state_test") 368 return newStateWithConfig(config, state, pv, app) 369 } 370 371 func newStateWithConfig( 372 thisConfig *cfg.Config, 373 state sm.State, 374 pv types.PrivValidator, 375 app abci.Application, 376 ) *State { 377 blockDB := dbm.NewMemDB() 378 return newStateWithConfigAndBlockStore(thisConfig, state, pv, app, blockDB) 379 } 380 381 func newStateWithConfigAndBlockStore( 382 thisConfig *cfg.Config, 383 state sm.State, 384 pv types.PrivValidator, 385 app abci.Application, 386 blockDB dbm.DB, 387 ) *State { 388 // Get BlockStore 389 blockStore := store.NewBlockStore(blockDB) 390 391 // one for mempool, one for consensus 392 mtx := new(tmsync.Mutex) 393 proxyAppConnMem := abcicli.NewLocalClient(mtx, app) 394 proxyAppConnCon := abcicli.NewLocalClient(mtx, app) 395 396 // Make Mempool 397 mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0) 398 mempool.SetLogger(log.TestingLogger().With("module", "mempool")) 399 if thisConfig.Consensus.WaitForTxs() { 400 mempool.EnableTxsAvailable() 401 } 402 403 evpool := sm.EmptyEvidencePool{} 404 405 // Make State 406 stateDB := blockDB 407 stateStore := sm.NewStore(stateDB) 408 if err := stateStore.Save(state); err != nil { // for save height 1's validators info 409 panic(err) 410 } 411 412 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyAppConnCon, mempool, evpool) 413 cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool) 414 cs.SetLogger(log.TestingLogger().With("module", "consensus")) 415 cs.SetPrivValidator(pv) 416 417 eventBus := types.NewEventBus() 418 eventBus.SetLogger(log.TestingLogger().With("module", "events")) 419 err := eventBus.Start() 420 if err != nil { 421 panic(err) 422 } 423 cs.SetEventBus(eventBus) 424 return cs 425 } 426 427 func loadPrivValidator(config *cfg.Config) *privval.FilePV { 428 privValidatorKeyFile := config.PrivValidatorKeyFile() 429 ensureDir(filepath.Dir(privValidatorKeyFile), 0700) 430 privValidatorStateFile := config.PrivValidatorStateFile() 431 privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) 432 privValidator.Reset() 433 return privValidator 434 } 435 436 func randState(nValidators int) (*State, []*validatorStub) { 437 // Get State 438 state, privVals := randGenesisState(nValidators, false, 10) 439 440 vss := make([]*validatorStub, nValidators) 441 442 cs := newState(state, privVals[0], counter.NewApplication(true)) 443 444 for i := 0; i < nValidators; i++ { 445 vss[i] = newValidatorStub(privVals[i], int32(i)) 446 } 447 // since cs1 starts at 1 448 incrementHeight(vss[1:]...) 449 450 return cs, vss 451 } 452 453 //------------------------------------------------------------------------------- 454 455 func ensureNoNewEvent(ch <-chan tmpubsub.Message, timeout time.Duration, 456 errorMessage string) { 457 select { 458 case <-time.After(timeout): 459 break 460 case <-ch: 461 panic(errorMessage) 462 } 463 } 464 465 func ensureNoNewEventOnChannel(ch <-chan tmpubsub.Message) { 466 ensureNoNewEvent( 467 ch, 468 ensureTimeout, 469 "We should be stuck waiting, not receiving new event on the channel") 470 } 471 472 func ensureNoNewRoundStep(stepCh <-chan tmpubsub.Message) { 473 ensureNoNewEvent( 474 stepCh, 475 ensureTimeout, 476 "We should be stuck waiting, not receiving NewRoundStep event") 477 } 478 479 func ensureNoNewUnlock(unlockCh <-chan tmpubsub.Message) { 480 ensureNoNewEvent( 481 unlockCh, 482 ensureTimeout, 483 "We should be stuck waiting, not receiving Unlock event") 484 } 485 486 func ensureNoNewTimeout(stepCh <-chan tmpubsub.Message, timeout int64) { 487 timeoutDuration := time.Duration(timeout*10) * time.Nanosecond 488 ensureNoNewEvent( 489 stepCh, 490 timeoutDuration, 491 "We should be stuck waiting, not receiving NewTimeout event") 492 } 493 494 func ensureNewEvent(ch <-chan tmpubsub.Message, height int64, round int32, timeout time.Duration, errorMessage string) { 495 select { 496 case <-time.After(timeout): 497 panic(errorMessage) 498 case msg := <-ch: 499 roundStateEvent, ok := msg.Data().(types.EventDataRoundState) 500 if !ok { 501 panic(fmt.Sprintf("expected a EventDataRoundState, got %T. Wrong subscription channel?", 502 msg.Data())) 503 } 504 if roundStateEvent.Height != height { 505 panic(fmt.Sprintf("expected height %v, got %v", height, roundStateEvent.Height)) 506 } 507 if roundStateEvent.Round != round { 508 panic(fmt.Sprintf("expected round %v, got %v", round, roundStateEvent.Round)) 509 } 510 // TODO: We could check also for a step at this point! 511 } 512 } 513 514 func ensureNewRound(roundCh <-chan tmpubsub.Message, height int64, round int32) { 515 select { 516 case <-time.After(ensureTimeout): 517 panic("Timeout expired while waiting for NewRound event") 518 case msg := <-roundCh: 519 newRoundEvent, ok := msg.Data().(types.EventDataNewRound) 520 if !ok { 521 panic(fmt.Sprintf("expected a EventDataNewRound, got %T. Wrong subscription channel?", 522 msg.Data())) 523 } 524 if newRoundEvent.Height != height { 525 panic(fmt.Sprintf("expected height %v, got %v", height, newRoundEvent.Height)) 526 } 527 if newRoundEvent.Round != round { 528 panic(fmt.Sprintf("expected round %v, got %v", round, newRoundEvent.Round)) 529 } 530 } 531 } 532 533 func ensureNewTimeout(timeoutCh <-chan tmpubsub.Message, height int64, round int32, timeout int64) { 534 timeoutDuration := time.Duration(timeout*10) * time.Nanosecond 535 ensureNewEvent(timeoutCh, height, round, timeoutDuration, 536 "Timeout expired while waiting for NewTimeout event") 537 } 538 539 func ensureNewProposal(proposalCh <-chan tmpubsub.Message, height int64, round int32) { 540 select { 541 case <-time.After(ensureTimeout): 542 panic("Timeout expired while waiting for NewProposal event") 543 case msg := <-proposalCh: 544 proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal) 545 if !ok { 546 panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?", 547 msg.Data())) 548 } 549 if proposalEvent.Height != height { 550 panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height)) 551 } 552 if proposalEvent.Round != round { 553 panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round)) 554 } 555 } 556 } 557 558 func ensureNewValidBlock(validBlockCh <-chan tmpubsub.Message, height int64, round int32) { 559 ensureNewEvent(validBlockCh, height, round, ensureTimeout, 560 "Timeout expired while waiting for NewValidBlock event") 561 } 562 563 func ensureNewBlock(blockCh <-chan tmpubsub.Message, height int64) { 564 select { 565 case <-time.After(ensureTimeout): 566 panic("Timeout expired while waiting for NewBlock event") 567 case msg := <-blockCh: 568 blockEvent, ok := msg.Data().(types.EventDataNewBlock) 569 if !ok { 570 panic(fmt.Sprintf("expected a EventDataNewBlock, got %T. Wrong subscription channel?", 571 msg.Data())) 572 } 573 if blockEvent.Block.Height != height { 574 panic(fmt.Sprintf("expected height %v, got %v", height, blockEvent.Block.Height)) 575 } 576 } 577 } 578 579 func ensureNewBlockHeader(blockCh <-chan tmpubsub.Message, height int64, blockHash tmbytes.HexBytes) { 580 select { 581 case <-time.After(ensureTimeout): 582 panic("Timeout expired while waiting for NewBlockHeader event") 583 case msg := <-blockCh: 584 blockHeaderEvent, ok := msg.Data().(types.EventDataNewBlockHeader) 585 if !ok { 586 panic(fmt.Sprintf("expected a EventDataNewBlockHeader, got %T. Wrong subscription channel?", 587 msg.Data())) 588 } 589 if blockHeaderEvent.Header.Height != height { 590 panic(fmt.Sprintf("expected height %v, got %v", height, blockHeaderEvent.Header.Height)) 591 } 592 if !bytes.Equal(blockHeaderEvent.Header.Hash(), blockHash) { 593 panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeaderEvent.Header.Hash())) 594 } 595 } 596 } 597 598 func ensureNewUnlock(unlockCh <-chan tmpubsub.Message, height int64, round int32) { 599 ensureNewEvent(unlockCh, height, round, ensureTimeout, 600 "Timeout expired while waiting for NewUnlock event") 601 } 602 603 func ensureProposal(proposalCh <-chan tmpubsub.Message, height int64, round int32, propID types.BlockID) { 604 select { 605 case <-time.After(ensureTimeout): 606 panic("Timeout expired while waiting for NewProposal event") 607 case msg := <-proposalCh: 608 proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal) 609 if !ok { 610 panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?", 611 msg.Data())) 612 } 613 if proposalEvent.Height != height { 614 panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height)) 615 } 616 if proposalEvent.Round != round { 617 panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round)) 618 } 619 if !proposalEvent.BlockID.Equals(propID) { 620 panic(fmt.Sprintf("Proposed block does not match expected block (%v != %v)", proposalEvent.BlockID, propID)) 621 } 622 } 623 } 624 625 func ensurePrecommit(voteCh <-chan tmpubsub.Message, height int64, round int32) { 626 ensureVote(voteCh, height, round, tmproto.PrecommitType) 627 } 628 629 func ensurePrevote(voteCh <-chan tmpubsub.Message, height int64, round int32) { 630 ensureVote(voteCh, height, round, tmproto.PrevoteType) 631 } 632 633 func ensureVote(voteCh <-chan tmpubsub.Message, height int64, round int32, 634 voteType tmproto.SignedMsgType) { 635 select { 636 case <-time.After(ensureTimeout): 637 panic("Timeout expired while waiting for NewVote event") 638 case msg := <-voteCh: 639 voteEvent, ok := msg.Data().(types.EventDataVote) 640 if !ok { 641 panic(fmt.Sprintf("expected a EventDataVote, got %T. Wrong subscription channel?", 642 msg.Data())) 643 } 644 vote := voteEvent.Vote 645 if vote.Height != height { 646 panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height)) 647 } 648 if vote.Round != round { 649 panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round)) 650 } 651 if vote.Type != voteType { 652 panic(fmt.Sprintf("expected type %v, got %v", voteType, vote.Type)) 653 } 654 } 655 } 656 657 func ensurePrecommitTimeout(ch <-chan tmpubsub.Message) { 658 select { 659 case <-time.After(ensureTimeout): 660 panic("Timeout expired while waiting for the Precommit to Timeout") 661 case <-ch: 662 } 663 } 664 665 func ensureNewEventOnChannel(ch <-chan tmpubsub.Message) { 666 select { 667 case <-time.After(ensureTimeout): 668 panic("Timeout expired while waiting for new activity on the channel") 669 case <-ch: 670 } 671 } 672 673 //------------------------------------------------------------------------------- 674 // consensus nets 675 676 // consensusLogger is a TestingLogger which uses a different 677 // color for each validator ("validator" key must exist). 678 func consensusLogger() log.Logger { 679 return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) term.FgBgColor { 680 for i := 0; i < len(keyvals)-1; i += 2 { 681 if keyvals[i] == "validator" { 682 return term.FgBgColor{Fg: term.Color(uint8(keyvals[i+1].(int) + 1))} 683 } 684 } 685 return term.FgBgColor{} 686 }).With("module", "consensus") 687 } 688 689 func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, 690 appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*State, cleanupFunc) { 691 genDoc, privVals := randGenesisDoc(nValidators, false, 30) 692 css := make([]*State, nValidators) 693 logger := consensusLogger() 694 configRootDirs := make([]string, 0, nValidators) 695 for i := 0; i < nValidators; i++ { 696 stateDB := dbm.NewMemDB() // each state needs its own db 697 stateStore := sm.NewStore(stateDB) 698 state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc) 699 thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) 700 configRootDirs = append(configRootDirs, thisConfig.RootDir) 701 for _, opt := range configOpts { 702 opt(thisConfig) 703 } 704 ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal 705 app := appFunc() 706 vals := types.TM2PB.ValidatorUpdates(state.Validators) 707 app.InitChain(abci.RequestInitChain{Validators: vals}) 708 709 css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) 710 css[i].SetTimeoutTicker(tickerFunc()) 711 css[i].SetLogger(logger.With("validator", i, "module", "consensus")) 712 } 713 return css, func() { 714 for _, dir := range configRootDirs { 715 os.RemoveAll(dir) 716 } 717 } 718 } 719 720 // nPeers = nValidators + nNotValidator 721 func randConsensusNetWithPeers( 722 nValidators, 723 nPeers int, 724 testName string, 725 tickerFunc func() TimeoutTicker, 726 appFunc func(string) abci.Application, 727 ) ([]*State, *types.GenesisDoc, *cfg.Config, cleanupFunc) { 728 genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower) 729 css := make([]*State, nPeers) 730 logger := consensusLogger() 731 var peer0Config *cfg.Config 732 configRootDirs := make([]string, 0, nPeers) 733 for i := 0; i < nPeers; i++ { 734 stateDB := dbm.NewMemDB() // each state needs its own db 735 stateStore := sm.NewStore(stateDB) 736 state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc) 737 thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) 738 configRootDirs = append(configRootDirs, thisConfig.RootDir) 739 ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal 740 if i == 0 { 741 peer0Config = thisConfig 742 } 743 var privVal types.PrivValidator 744 if i < nValidators { 745 privVal = privVals[i] 746 } else { 747 tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") 748 if err != nil { 749 panic(err) 750 } 751 tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") 752 if err != nil { 753 panic(err) 754 } 755 756 privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) 757 } 758 759 app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i))) 760 vals := types.TM2PB.ValidatorUpdates(state.Validators) 761 if _, ok := app.(*kvstore.PersistentKVStoreApplication); ok { 762 // simulate handshake, receive app version. If don't do this, replay test will fail 763 state.Version.Consensus.App = kvstore.ProtocolVersion 764 } 765 app.InitChain(abci.RequestInitChain{Validators: vals}) 766 // sm.SaveState(stateDB,state) //height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above 767 768 css[i] = newStateWithConfig(thisConfig, state, privVal, app) 769 css[i].SetTimeoutTicker(tickerFunc()) 770 css[i].SetLogger(logger.With("validator", i, "module", "consensus")) 771 } 772 return css, genDoc, peer0Config, func() { 773 for _, dir := range configRootDirs { 774 os.RemoveAll(dir) 775 } 776 } 777 } 778 779 func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { 780 for i, s := range switches { 781 if peer.NodeInfo().ID() == s.NodeInfo().ID() { 782 return i 783 } 784 } 785 panic("didnt find peer in switches") 786 } 787 788 //------------------------------------------------------------------------------- 789 // genesis 790 791 func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { 792 validators := make([]types.GenesisValidator, numValidators) 793 privValidators := make([]types.PrivValidator, numValidators) 794 for i := 0; i < numValidators; i++ { 795 val, privVal := types.RandValidator(randPower, minPower) 796 validators[i] = types.GenesisValidator{ 797 PubKey: val.PubKey, 798 Power: val.VotingPower, 799 } 800 privValidators[i] = privVal 801 } 802 sort.Sort(types.PrivValidatorsByAddress(privValidators)) 803 804 return &types.GenesisDoc{ 805 GenesisTime: tmtime.Now(), 806 InitialHeight: 1, 807 ChainID: config.ChainID(), 808 Validators: validators, 809 }, privValidators 810 } 811 812 func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) { 813 genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) 814 s0, _ := sm.MakeGenesisState(genDoc) 815 return s0, privValidators 816 } 817 818 //------------------------------------ 819 // mock ticker 820 821 func newMockTickerFunc(onlyOnce bool) func() TimeoutTicker { 822 return func() TimeoutTicker { 823 return &mockTicker{ 824 c: make(chan timeoutInfo, 10), 825 onlyOnce: onlyOnce, 826 } 827 } 828 } 829 830 // mock ticker only fires on RoundStepNewHeight 831 // and only once if onlyOnce=true 832 type mockTicker struct { 833 c chan timeoutInfo 834 835 mtx sync.Mutex 836 onlyOnce bool 837 fired bool 838 } 839 840 func (m *mockTicker) Start() error { 841 return nil 842 } 843 844 func (m *mockTicker) Stop() error { 845 return nil 846 } 847 848 func (m *mockTicker) ScheduleTimeout(ti timeoutInfo) { 849 m.mtx.Lock() 850 defer m.mtx.Unlock() 851 if m.onlyOnce && m.fired { 852 return 853 } 854 if ti.Step == cstypes.RoundStepNewHeight { 855 m.c <- ti 856 m.fired = true 857 } 858 } 859 860 func (m *mockTicker) Chan() <-chan timeoutInfo { 861 return m.c 862 } 863 864 func (*mockTicker) SetLogger(log.Logger) {} 865 866 //------------------------------------ 867 868 func newCounter() abci.Application { 869 return counter.NewApplication(true) 870 } 871 872 func newPersistentKVStore() abci.Application { 873 dir, err := ioutil.TempDir("", "persistent-kvstore") 874 if err != nil { 875 panic(err) 876 } 877 return kvstore.NewPersistentKVStoreApplication(dir) 878 } 879 880 func newPersistentKVStoreWithPath(dbDir string) abci.Application { 881 return kvstore.NewPersistentKVStoreApplication(dbDir) 882 } 883 884 func signDataIsEqual(v1 *types.Vote, v2 *tmproto.Vote) bool { 885 if v1 == nil || v2 == nil { 886 return false 887 } 888 889 return v1.Type == v2.Type && 890 bytes.Equal(v1.BlockID.Hash, v2.BlockID.GetHash()) && 891 v1.Height == v2.GetHeight() && 892 v1.Round == v2.Round && 893 bytes.Equal(v1.ValidatorAddress.Bytes(), v2.GetValidatorAddress()) && 894 v1.ValidatorIndex == v2.GetValidatorIndex() 895 }