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