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