github.com/vipernet-xyz/tendermint-core@v0.32.0/consensus/state_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 cstypes "github.com/tendermint/tendermint/consensus/types" 14 "github.com/tendermint/tendermint/libs/log" 15 tmpubsub "github.com/tendermint/tendermint/libs/pubsub" 16 tmrand "github.com/tendermint/tendermint/libs/rand" 17 p2pmock "github.com/tendermint/tendermint/p2p/mock" 18 "github.com/tendermint/tendermint/types" 19 ) 20 21 /* 22 23 ProposeSuite 24 x * TestProposerSelection0 - round robin ordering, round 0 25 x * TestProposerSelection2 - round robin ordering, round 2++ 26 x * TestEnterProposeNoValidator - timeout into prevote round 27 x * TestEnterPropose - finish propose without timing out (we have the proposal) 28 x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevote and precommit nil 29 FullRoundSuite 30 x * TestFullRound1 - 1 val, full successful round 31 x * TestFullRoundNil - 1 val, full round of nil 32 x * TestFullRound2 - 2 vals, both required for full round 33 LockSuite 34 x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first. 35 x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka 36 x * TestLockPOLUnlock - 4 vals, one precommits, other 3 polka nil at next round, so we unlock and precomit nil 37 x * TestLockPOLSafety1 - 4 vals. We shouldn't change lock based on polka at earlier round 38 x * TestLockPOLSafety2 - 4 vals. After unlocking, we shouldn't relock based on polka at earlier round 39 * TestNetworkLock - once +1/3 precommits, network should be locked 40 * TestNetworkLockPOL - once +1/3 precommits, the block with more recent polka is committed 41 SlashingSuite 42 x * TestSlashingPrevotes - a validator prevoting twice in a round gets slashed 43 x * TestSlashingPrecommits - a validator precomitting twice in a round gets slashed 44 CatchupSuite 45 * TestCatchup - if we might be behind and we've seen any 2/3 prevotes, round skip to new round, precommit, or prevote 46 HaltSuite 47 x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we should still commit 48 49 */ 50 51 //---------------------------------------------------------------------------------------------------- 52 // ProposeSuite 53 54 func TestStateProposerSelection0(t *testing.T) { 55 cs1, vss := randState(4) 56 height, round := cs1.Height, cs1.Round 57 58 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 59 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 60 61 startTestRound(cs1, height, round) 62 63 // Wait for new round so proposer is set. 64 ensureNewRound(newRoundCh, height, round) 65 66 // Commit a block and ensure proposer for the next height is correct. 67 prop := cs1.GetRoundState().Validators.GetProposer() 68 pv, err := cs1.privValidator.GetPubKey() 69 require.NoError(t, err) 70 address := pv.Address() 71 if !bytes.Equal(prop.Address, address) { 72 t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) 73 } 74 75 // Wait for complete proposal. 76 ensureNewProposal(proposalCh, height, round) 77 78 rs := cs1.GetRoundState() 79 signAddVotes(cs1, types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...) 80 81 // Wait for new round so next validator is set. 82 ensureNewRound(newRoundCh, height+1, 0) 83 84 prop = cs1.GetRoundState().Validators.GetProposer() 85 pv1, err := vss[1].GetPubKey() 86 require.NoError(t, err) 87 addr := pv1.Address() 88 if !bytes.Equal(prop.Address, addr) { 89 panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address)) 90 } 91 } 92 93 // Now let's do it all again, but starting from round 2 instead of 0 94 func TestStateProposerSelection2(t *testing.T) { 95 cs1, vss := randState(4) // test needs more work for more than 3 validators 96 height := cs1.Height 97 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 98 99 // this time we jump in at round 2 100 incrementRound(vss[1:]...) 101 incrementRound(vss[1:]...) 102 103 round := 2 104 startTestRound(cs1, height, round) 105 106 ensureNewRound(newRoundCh, height, round) // wait for the new round 107 108 // everyone just votes nil. we get a new proposer each round 109 for i := 0; i < len(vss); i++ { 110 prop := cs1.GetRoundState().Validators.GetProposer() 111 pvk, err := vss[(i+round)%len(vss)].GetPubKey() 112 require.NoError(t, err) 113 addr := pvk.Address() 114 correctProposer := addr 115 if !bytes.Equal(prop.Address, correctProposer) { 116 panic(fmt.Sprintf( 117 "expected RoundState.Validators.GetProposer() to be validator %d. Got %X", 118 (i+2)%len(vss), 119 prop.Address)) 120 } 121 122 rs := cs1.GetRoundState() 123 signAddVotes(cs1, types.PrecommitType, nil, rs.ProposalBlockParts.Header(), vss[1:]...) 124 ensureNewRound(newRoundCh, height, i+round+1) // wait for the new round event each round 125 incrementRound(vss[1:]...) 126 } 127 128 } 129 130 // a non-validator should timeout into the prevote round 131 func TestStateEnterProposeNoPrivValidator(t *testing.T) { 132 cs, _ := randState(1) 133 cs.SetPrivValidator(nil) 134 height, round := cs.Height, cs.Round 135 136 // Listen for propose timeout event 137 timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose) 138 139 startTestRound(cs, height, round) 140 141 // if we're not a validator, EnterPropose should timeout 142 ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds()) 143 144 if cs.GetRoundState().Proposal != nil { 145 t.Error("Expected to make no proposal, since no privValidator") 146 } 147 } 148 149 // a validator should not timeout of the prevote round (TODO: unless the block is really big!) 150 func TestStateEnterProposeYesPrivValidator(t *testing.T) { 151 cs, _ := randState(1) 152 height, round := cs.Height, cs.Round 153 154 // Listen for propose timeout event 155 156 timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose) 157 proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) 158 159 cs.enterNewRound(height, round) 160 cs.startRoutines(3) 161 162 ensureNewProposal(proposalCh, height, round) 163 164 // Check that Proposal, ProposalBlock, ProposalBlockParts are set. 165 rs := cs.GetRoundState() 166 if rs.Proposal == nil { 167 t.Error("rs.Proposal should be set") 168 } 169 if rs.ProposalBlock == nil { 170 t.Error("rs.ProposalBlock should be set") 171 } 172 if rs.ProposalBlockParts.Total() == 0 { 173 t.Error("rs.ProposalBlockParts should be set") 174 } 175 176 // if we're a validator, enterPropose should not timeout 177 ensureNoNewTimeout(timeoutCh, cs.config.TimeoutPropose.Nanoseconds()) 178 } 179 180 func TestStateBadProposal(t *testing.T) { 181 cs1, vss := randState(2) 182 height, round := cs1.Height, cs1.Round 183 vs2 := vss[1] 184 185 partSize := types.BlockPartSizeBytes 186 187 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 188 voteCh := subscribe(cs1.eventBus, types.EventQueryVote) 189 190 propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2) 191 192 // make the second validator the proposer by incrementing round 193 round++ 194 incrementRound(vss[1:]...) 195 196 // make the block bad by tampering with statehash 197 stateHash := propBlock.AppHash 198 if len(stateHash) == 0 { 199 stateHash = make([]byte, 32) 200 } 201 stateHash[0] = (stateHash[0] + 1) % 255 202 propBlock.AppHash = stateHash 203 propBlockParts := propBlock.MakePartSet(partSize) 204 blockID := types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} 205 proposal := types.NewProposal(vs2.Height, round, -1, blockID) 206 if err := vs2.SignProposal(config.ChainID(), proposal); err != nil { 207 t.Fatal("failed to sign bad proposal", err) 208 } 209 210 // set the proposal block 211 if err := cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { 212 t.Fatal(err) 213 } 214 215 // start the machine 216 startTestRound(cs1, height, round) 217 218 // wait for proposal 219 ensureProposal(proposalCh, height, round, blockID) 220 221 // wait for prevote 222 ensurePrevote(voteCh, height, round) 223 validatePrevote(t, cs1, round, vss[0], nil) 224 225 // add bad prevote from vs2 and wait for it 226 signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) 227 ensurePrevote(voteCh, height, round) 228 229 // wait for precommit 230 ensurePrecommit(voteCh, height, round) 231 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 232 signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) 233 } 234 235 //---------------------------------------------------------------------------------------------------- 236 // FullRoundSuite 237 238 // propose, prevote, and precommit a block 239 func TestStateFullRound1(t *testing.T) { 240 cs, vss := randState(1) 241 height, round := cs.Height, cs.Round 242 243 // NOTE: buffer capacity of 0 ensures we can validate prevote and last commit 244 // before consensus can move to the next height (and cause a race condition) 245 cs.eventBus.Stop() 246 eventBus := types.NewEventBusWithBufferCapacity(0) 247 eventBus.SetLogger(log.TestingLogger().With("module", "events")) 248 cs.SetEventBus(eventBus) 249 eventBus.Start() 250 251 voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote) 252 propCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) 253 newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) 254 255 // Maybe it would be better to call explicitly startRoutines(4) 256 startTestRound(cs, height, round) 257 258 ensureNewRound(newRoundCh, height, round) 259 260 ensureNewProposal(propCh, height, round) 261 propBlockHash := cs.GetRoundState().ProposalBlock.Hash() 262 263 ensurePrevote(voteCh, height, round) // wait for prevote 264 validatePrevote(t, cs, round, vss[0], propBlockHash) 265 266 ensurePrecommit(voteCh, height, round) // wait for precommit 267 268 // we're going to roll right into new height 269 ensureNewRound(newRoundCh, height+1, 0) 270 271 validateLastPrecommit(t, cs, vss[0], propBlockHash) 272 } 273 274 // nil is proposed, so prevote and precommit nil 275 func TestStateFullRoundNil(t *testing.T) { 276 cs, vss := randState(1) 277 height, round := cs.Height, cs.Round 278 279 voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote) 280 281 cs.enterPrevote(height, round) 282 cs.startRoutines(4) 283 284 ensurePrevote(voteCh, height, round) // prevote 285 ensurePrecommit(voteCh, height, round) // precommit 286 287 // should prevote and precommit nil 288 validatePrevoteAndPrecommit(t, cs, round, -1, vss[0], nil, nil) 289 } 290 291 // run through propose, prevote, precommit commit with two validators 292 // where the first validator has to wait for votes from the second 293 func TestStateFullRound2(t *testing.T) { 294 cs1, vss := randState(2) 295 vs2 := vss[1] 296 height, round := cs1.Height, cs1.Round 297 298 voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote) 299 newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock) 300 301 // start round and wait for propose and prevote 302 startTestRound(cs1, height, round) 303 304 ensurePrevote(voteCh, height, round) // prevote 305 306 // we should be stuck in limbo waiting for more prevotes 307 rs := cs1.GetRoundState() 308 propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header() 309 310 // prevote arrives from vs2: 311 signAddVotes(cs1, types.PrevoteType, propBlockHash, propPartsHeader, vs2) 312 ensurePrevote(voteCh, height, round) // prevote 313 314 ensurePrecommit(voteCh, height, round) //precommit 315 // the proposed block should now be locked and our precommit added 316 validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash) 317 318 // we should be stuck in limbo waiting for more precommits 319 320 // precommit arrives from vs2: 321 signAddVotes(cs1, types.PrecommitType, propBlockHash, propPartsHeader, vs2) 322 ensurePrecommit(voteCh, height, round) 323 324 // wait to finish commit, propose in next height 325 ensureNewBlock(newBlockCh, height) 326 } 327 328 //------------------------------------------------------------------------------------------ 329 // LockSuite 330 331 // two validators, 4 rounds. 332 // two vals take turns proposing. val1 locks on first one, precommits nil on everything else 333 func TestStateLockNoPOL(t *testing.T) { 334 cs1, vss := randState(2) 335 vs2 := vss[1] 336 height, round := cs1.Height, cs1.Round 337 338 partSize := types.BlockPartSizeBytes 339 340 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 341 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 342 voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote) 343 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 344 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 345 346 /* 347 Round1 (cs1, B) // B B // B B2 348 */ 349 350 // start round and wait for prevote 351 cs1.enterNewRound(height, round) 352 cs1.startRoutines(0) 353 354 ensureNewRound(newRoundCh, height, round) 355 356 ensureNewProposal(proposalCh, height, round) 357 roundState := cs1.GetRoundState() 358 theBlockHash := roundState.ProposalBlock.Hash() 359 thePartSetHeader := roundState.ProposalBlockParts.Header() 360 361 ensurePrevote(voteCh, height, round) // prevote 362 363 // we should now be stuck in limbo forever, waiting for more prevotes 364 // prevote arrives from vs2: 365 signAddVotes(cs1, types.PrevoteType, theBlockHash, thePartSetHeader, vs2) 366 ensurePrevote(voteCh, height, round) // prevote 367 368 ensurePrecommit(voteCh, height, round) // precommit 369 // the proposed block should now be locked and our precommit added 370 validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) 371 372 // we should now be stuck in limbo forever, waiting for more precommits 373 // lets add one for a different block 374 hash := make([]byte, len(theBlockHash)) 375 copy(hash, theBlockHash) 376 hash[0] = (hash[0] + 1) % 255 377 signAddVotes(cs1, types.PrecommitType, hash, thePartSetHeader, vs2) 378 ensurePrecommit(voteCh, height, round) // precommit 379 380 // (note we're entering precommit for a second time this round) 381 // but with invalid args. then we enterPrecommitWait, and the timeout to new round 382 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 383 384 /// 385 386 round++ // moving to the next round 387 ensureNewRound(newRoundCh, height, round) 388 t.Log("#### ONTO ROUND 1") 389 /* 390 Round2 (cs1, B) // B B2 391 */ 392 393 incrementRound(vs2) 394 395 // now we're on a new round and not the proposer, so wait for timeout 396 ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) 397 398 rs := cs1.GetRoundState() 399 400 if rs.ProposalBlock != nil { 401 panic("Expected proposal block to be nil") 402 } 403 404 // wait to finish prevote 405 ensurePrevote(voteCh, height, round) 406 // we should have prevoted our locked block 407 validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash()) 408 409 // add a conflicting prevote from the other validator 410 signAddVotes(cs1, types.PrevoteType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2) 411 ensurePrevote(voteCh, height, round) 412 413 // now we're going to enter prevote again, but with invalid args 414 // and then prevote wait, which should timeout. then wait for precommit 415 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) 416 417 ensurePrecommit(voteCh, height, round) // precommit 418 // the proposed block should still be locked and our precommit added 419 // we should precommit nil and be locked on the proposal 420 validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) 421 422 // add conflicting precommit from vs2 423 signAddVotes(cs1, types.PrecommitType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2) 424 ensurePrecommit(voteCh, height, round) 425 426 // (note we're entering precommit for a second time this round, but with invalid args 427 // then we enterPrecommitWait and timeout into NewRound 428 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 429 430 round++ // entering new round 431 ensureNewRound(newRoundCh, height, round) 432 t.Log("#### ONTO ROUND 2") 433 /* 434 Round3 (vs2, _) // B, B2 435 */ 436 437 incrementRound(vs2) 438 439 ensureNewProposal(proposalCh, height, round) 440 rs = cs1.GetRoundState() 441 442 // now we're on a new round and are the proposer 443 if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { 444 panic(fmt.Sprintf( 445 "Expected proposal block to be locked block. Got %v, Expected %v", 446 rs.ProposalBlock, 447 rs.LockedBlock)) 448 } 449 450 ensurePrevote(voteCh, height, round) // prevote 451 validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash()) 452 453 signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) 454 ensurePrevote(voteCh, height, round) 455 456 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) 457 ensurePrecommit(voteCh, height, round) // precommit 458 459 validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal 460 461 signAddVotes( 462 cs1, 463 types.PrecommitType, 464 hash, 465 rs.ProposalBlock.MakePartSet(partSize).Header(), 466 vs2) // NOTE: conflicting precommits at same height 467 ensurePrecommit(voteCh, height, round) 468 469 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 470 471 cs2, _ := randState(2) // needed so generated block is different than locked block 472 // before we time out into new round, set next proposal block 473 prop, propBlock := decideProposal(cs2, vs2, vs2.Height, vs2.Round+1) 474 if prop == nil || propBlock == nil { 475 t.Fatal("Failed to create proposal block with vs2") 476 } 477 478 incrementRound(vs2) 479 480 round++ // entering new round 481 ensureNewRound(newRoundCh, height, round) 482 t.Log("#### ONTO ROUND 3") 483 /* 484 Round4 (vs2, C) // B C // B C 485 */ 486 487 // now we're on a new round and not the proposer 488 // so set the proposal block 489 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(partSize), ""); err != nil { 490 t.Fatal(err) 491 } 492 493 ensureNewProposal(proposalCh, height, round) 494 ensurePrevote(voteCh, height, round) // prevote 495 // prevote for locked block (not proposal) 496 validatePrevote(t, cs1, 3, vss[0], cs1.LockedBlock.Hash()) 497 498 // prevote for proposed block 499 signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) 500 ensurePrevote(voteCh, height, round) 501 502 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) 503 ensurePrecommit(voteCh, height, round) 504 validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal 505 506 signAddVotes( 507 cs1, 508 types.PrecommitType, 509 propBlock.Hash(), 510 propBlock.MakePartSet(partSize).Header(), 511 vs2) // NOTE: conflicting precommits at same height 512 ensurePrecommit(voteCh, height, round) 513 } 514 515 // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka 516 func TestStateLockPOLRelock(t *testing.T) { 517 cs1, vss := randState(4) 518 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 519 height, round := cs1.Height, cs1.Round 520 521 partSize := types.BlockPartSizeBytes 522 523 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 524 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 525 pv1, err := cs1.privValidator.GetPubKey() 526 require.NoError(t, err) 527 addr := pv1.Address() 528 voteCh := subscribeToVoter(cs1, addr) 529 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 530 newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) 531 532 // everything done from perspective of cs1 533 534 /* 535 Round1 (cs1, B) // B B B B// B nil B nil 536 537 eg. vs2 and vs4 didn't see the 2/3 prevotes 538 */ 539 540 // start round and wait for propose and prevote 541 startTestRound(cs1, height, round) 542 543 ensureNewRound(newRoundCh, height, round) 544 ensureNewProposal(proposalCh, height, round) 545 rs := cs1.GetRoundState() 546 theBlockHash := rs.ProposalBlock.Hash() 547 theBlockParts := rs.ProposalBlockParts.Header() 548 549 ensurePrevote(voteCh, height, round) // prevote 550 551 signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4) 552 553 ensurePrecommit(voteCh, height, round) // our precommit 554 // the proposed block should now be locked and our precommit added 555 validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) 556 557 // add precommits from the rest 558 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4) 559 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) 560 561 // before we timeout to the new round set the new proposal 562 prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) 563 propBlockParts := propBlock.MakePartSet(partSize) 564 propBlockHash := propBlock.Hash() 565 566 incrementRound(vs2, vs3, vs4) 567 568 // timeout to new round 569 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 570 571 round++ // moving to the next round 572 //XXX: this isnt guaranteed to get there before the timeoutPropose ... 573 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 574 t.Fatal(err) 575 } 576 577 ensureNewRound(newRoundCh, height, round) 578 t.Log("### ONTO ROUND 1") 579 580 /* 581 Round2 (vs2, C) // B C C C // C C C _) 582 583 cs1 changes lock! 584 */ 585 586 // now we're on a new round and not the proposer 587 // but we should receive the proposal 588 ensureNewProposal(proposalCh, height, round) 589 590 // go to prevote, prevote for locked block (not proposal), move on 591 ensurePrevote(voteCh, height, round) 592 validatePrevote(t, cs1, round, vss[0], theBlockHash) 593 594 // now lets add prevotes from everyone else for the new block 595 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) 596 597 ensurePrecommit(voteCh, height, round) 598 // we should have unlocked and locked on the new block 599 validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash) 600 601 signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3) 602 ensureNewBlockHeader(newBlockCh, height, propBlockHash) 603 604 ensureNewRound(newRoundCh, height+1, 0) 605 } 606 607 // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka 608 func TestStateLockPOLUnlock(t *testing.T) { 609 cs1, vss := randState(4) 610 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 611 height, round := cs1.Height, cs1.Round 612 613 partSize := types.BlockPartSizeBytes 614 615 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 616 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 617 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 618 unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) 619 pv1, err := cs1.privValidator.GetPubKey() 620 require.NoError(t, err) 621 addr := pv1.Address() 622 voteCh := subscribeToVoter(cs1, addr) 623 624 // everything done from perspective of cs1 625 626 /* 627 Round1 (cs1, B) // B B B B // B nil B nil 628 629 eg. didn't see the 2/3 prevotes 630 */ 631 632 // start round and wait for propose and prevote 633 startTestRound(cs1, height, round) 634 ensureNewRound(newRoundCh, height, round) 635 636 ensureNewProposal(proposalCh, height, round) 637 rs := cs1.GetRoundState() 638 theBlockHash := rs.ProposalBlock.Hash() 639 theBlockParts := rs.ProposalBlockParts.Header() 640 641 ensurePrevote(voteCh, height, round) 642 validatePrevote(t, cs1, round, vss[0], theBlockHash) 643 644 signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4) 645 646 ensurePrecommit(voteCh, height, round) 647 // the proposed block should now be locked and our precommit added 648 validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) 649 650 // add precommits from the rest 651 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4) 652 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) 653 654 // before we time out into new round, set next proposal block 655 prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) 656 propBlockParts := propBlock.MakePartSet(partSize) 657 658 // timeout to new round 659 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 660 rs = cs1.GetRoundState() 661 lockedBlockHash := rs.LockedBlock.Hash() 662 663 incrementRound(vs2, vs3, vs4) 664 round++ // moving to the next round 665 666 ensureNewRound(newRoundCh, height, round) 667 t.Log("#### ONTO ROUND 1") 668 /* 669 Round2 (vs2, C) // B nil nil nil // nil nil nil _ 670 671 cs1 unlocks! 672 */ 673 //XXX: this isnt guaranteed to get there before the timeoutPropose ... 674 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 675 t.Fatal(err) 676 } 677 678 ensureNewProposal(proposalCh, height, round) 679 680 // go to prevote, prevote for locked block (not proposal) 681 ensurePrevote(voteCh, height, round) 682 validatePrevote(t, cs1, round, vss[0], lockedBlockHash) 683 // now lets add prevotes from everyone else for nil (a polka!) 684 signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 685 686 // the polka makes us unlock and precommit nil 687 ensureNewUnlock(unlockCh, height, round) 688 ensurePrecommit(voteCh, height, round) 689 690 // we should have unlocked and committed nil 691 // NOTE: since we don't relock on nil, the lock round is -1 692 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 693 694 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3) 695 ensureNewRound(newRoundCh, height, round+1) 696 } 697 698 // 4 vals 699 // a polka at round 1 but we miss it 700 // then a polka at round 2 that we lock on 701 // then we see the polka from round 1 but shouldn't unlock 702 func TestStateLockPOLSafety1(t *testing.T) { 703 cs1, vss := randState(4) 704 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 705 height, round := cs1.Height, cs1.Round 706 707 partSize := types.BlockPartSizeBytes 708 709 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 710 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 711 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 712 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 713 pv1, err := cs1.privValidator.GetPubKey() 714 require.NoError(t, err) 715 addr := pv1.Address() 716 voteCh := subscribeToVoter(cs1, addr) 717 718 // start round and wait for propose and prevote 719 startTestRound(cs1, cs1.Height, round) 720 ensureNewRound(newRoundCh, height, round) 721 722 ensureNewProposal(proposalCh, height, round) 723 rs := cs1.GetRoundState() 724 propBlock := rs.ProposalBlock 725 726 ensurePrevote(voteCh, height, round) 727 validatePrevote(t, cs1, round, vss[0], propBlock.Hash()) 728 729 // the others sign a polka but we don't see it 730 prevotes := signVotes(types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4) 731 732 t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash())) 733 734 // we do see them precommit nil 735 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 736 737 // cs1 precommit nil 738 ensurePrecommit(voteCh, height, round) 739 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 740 741 t.Log("### ONTO ROUND 1") 742 743 prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) 744 propBlockHash := propBlock.Hash() 745 propBlockParts := propBlock.MakePartSet(partSize) 746 747 incrementRound(vs2, vs3, vs4) 748 749 round++ // moving to the next round 750 ensureNewRound(newRoundCh, height, round) 751 752 //XXX: this isnt guaranteed to get there before the timeoutPropose ... 753 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 754 t.Fatal(err) 755 } 756 /*Round2 757 // we timeout and prevote our lock 758 // a polka happened but we didn't see it! 759 */ 760 761 ensureNewProposal(proposalCh, height, round) 762 763 rs = cs1.GetRoundState() 764 765 if rs.LockedBlock != nil { 766 panic("we should not be locked!") 767 } 768 t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash)) 769 770 // go to prevote, prevote for proposal block 771 ensurePrevote(voteCh, height, round) 772 validatePrevote(t, cs1, round, vss[0], propBlockHash) 773 774 // now we see the others prevote for it, so we should lock on it 775 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) 776 777 ensurePrecommit(voteCh, height, round) 778 // we should have precommitted 779 validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash) 780 781 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 782 783 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 784 785 incrementRound(vs2, vs3, vs4) 786 round++ // moving to the next round 787 788 ensureNewRound(newRoundCh, height, round) 789 790 t.Log("### ONTO ROUND 2") 791 /*Round3 792 we see the polka from round 1 but we shouldn't unlock! 793 */ 794 795 // timeout of propose 796 ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) 797 798 // finish prevote 799 ensurePrevote(voteCh, height, round) 800 // we should prevote what we're locked on 801 validatePrevote(t, cs1, round, vss[0], propBlockHash) 802 803 newStepCh := subscribe(cs1.eventBus, types.EventQueryNewRoundStep) 804 805 // before prevotes from the previous round are added 806 // add prevotes from the earlier round 807 addVotes(cs1, prevotes...) 808 809 t.Log("Done adding prevotes!") 810 811 ensureNoNewRoundStep(newStepCh) 812 } 813 814 // 4 vals. 815 // polka P0 at R0, P1 at R1, and P2 at R2, 816 // we lock on P0 at R0, don't see P1, and unlock using P2 at R2 817 // then we should make sure we don't lock using P1 818 819 // What we want: 820 // dont see P0, lock on P1 at R1, dont unlock using P0 at R2 821 func TestStateLockPOLSafety2(t *testing.T) { 822 cs1, vss := randState(4) 823 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 824 height, round := cs1.Height, cs1.Round 825 826 partSize := types.BlockPartSizeBytes 827 828 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 829 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 830 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 831 unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) 832 pv1, err := cs1.privValidator.GetPubKey() 833 require.NoError(t, err) 834 addr := pv1.Address() 835 voteCh := subscribeToVoter(cs1, addr) 836 837 // the block for R0: gets polkad but we miss it 838 // (even though we signed it, shhh) 839 _, propBlock0 := decideProposal(cs1, vss[0], height, round) 840 propBlockHash0 := propBlock0.Hash() 841 propBlockParts0 := propBlock0.MakePartSet(partSize) 842 propBlockID0 := types.BlockID{Hash: propBlockHash0, PartsHeader: propBlockParts0.Header()} 843 844 // the others sign a polka but we don't see it 845 prevotes := signVotes(types.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4) 846 847 // the block for round 1 848 prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) 849 propBlockHash1 := propBlock1.Hash() 850 propBlockParts1 := propBlock1.MakePartSet(partSize) 851 852 incrementRound(vs2, vs3, vs4) 853 854 round++ // moving to the next round 855 t.Log("### ONTO Round 1") 856 // jump in at round 1 857 startTestRound(cs1, height, round) 858 ensureNewRound(newRoundCh, height, round) 859 860 if err := cs1.SetProposalAndBlock(prop1, propBlock1, propBlockParts1, "some peer"); err != nil { 861 t.Fatal(err) 862 } 863 ensureNewProposal(proposalCh, height, round) 864 865 ensurePrevote(voteCh, height, round) 866 validatePrevote(t, cs1, round, vss[0], propBlockHash1) 867 868 signAddVotes(cs1, types.PrevoteType, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4) 869 870 ensurePrecommit(voteCh, height, round) 871 // the proposed block should now be locked and our precommit added 872 validatePrecommit(t, cs1, round, round, vss[0], propBlockHash1, propBlockHash1) 873 874 // add precommits from the rest 875 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs4) 876 signAddVotes(cs1, types.PrecommitType, propBlockHash1, propBlockParts1.Header(), vs3) 877 878 incrementRound(vs2, vs3, vs4) 879 880 // timeout of precommit wait to new round 881 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 882 883 round++ // moving to the next round 884 // in round 2 we see the polkad block from round 0 885 newProp := types.NewProposal(height, round, 0, propBlockID0) 886 if err := vs3.SignProposal(config.ChainID(), newProp); err != nil { 887 t.Fatal(err) 888 } 889 if err := cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer"); err != nil { 890 t.Fatal(err) 891 } 892 893 // Add the pol votes 894 addVotes(cs1, prevotes...) 895 896 ensureNewRound(newRoundCh, height, round) 897 t.Log("### ONTO Round 2") 898 /*Round2 899 // now we see the polka from round 1, but we shouldnt unlock 900 */ 901 ensureNewProposal(proposalCh, height, round) 902 903 ensureNoNewUnlock(unlockCh) 904 ensurePrevote(voteCh, height, round) 905 validatePrevote(t, cs1, round, vss[0], propBlockHash1) 906 907 } 908 909 // 4 vals. 910 // polka P0 at R0 for B0. We lock B0 on P0 at R0. P0 unlocks value at R1. 911 912 // What we want: 913 // P0 proposes B0 at R3. 914 func TestProposeValidBlock(t *testing.T) { 915 cs1, vss := randState(4) 916 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 917 height, round := cs1.Height, cs1.Round 918 919 partSize := types.BlockPartSizeBytes 920 921 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 922 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 923 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 924 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 925 unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) 926 pv1, err := cs1.privValidator.GetPubKey() 927 require.NoError(t, err) 928 addr := pv1.Address() 929 voteCh := subscribeToVoter(cs1, addr) 930 931 // start round and wait for propose and prevote 932 startTestRound(cs1, cs1.Height, round) 933 ensureNewRound(newRoundCh, height, round) 934 935 ensureNewProposal(proposalCh, height, round) 936 rs := cs1.GetRoundState() 937 propBlock := rs.ProposalBlock 938 propBlockHash := propBlock.Hash() 939 940 ensurePrevote(voteCh, height, round) 941 validatePrevote(t, cs1, round, vss[0], propBlockHash) 942 943 // the others sign a polka 944 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4) 945 946 ensurePrecommit(voteCh, height, round) 947 // we should have precommitted 948 validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash) 949 950 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 951 952 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 953 954 incrementRound(vs2, vs3, vs4) 955 round++ // moving to the next round 956 957 ensureNewRound(newRoundCh, height, round) 958 959 t.Log("### ONTO ROUND 2") 960 961 // timeout of propose 962 ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) 963 964 ensurePrevote(voteCh, height, round) 965 validatePrevote(t, cs1, round, vss[0], propBlockHash) 966 967 signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 968 969 ensureNewUnlock(unlockCh, height, round) 970 971 ensurePrecommit(voteCh, height, round) 972 // we should have precommitted 973 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 974 975 incrementRound(vs2, vs3, vs4) 976 incrementRound(vs2, vs3, vs4) 977 978 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 979 980 round += 2 // moving to the next round 981 982 ensureNewRound(newRoundCh, height, round) 983 t.Log("### ONTO ROUND 3") 984 985 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 986 987 round++ // moving to the next round 988 989 ensureNewRound(newRoundCh, height, round) 990 991 t.Log("### ONTO ROUND 4") 992 993 ensureNewProposal(proposalCh, height, round) 994 995 rs = cs1.GetRoundState() 996 assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), propBlockHash)) 997 assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), rs.ValidBlock.Hash())) 998 assert.True(t, rs.Proposal.POLRound == rs.ValidRound) 999 assert.True(t, bytes.Equal(rs.Proposal.BlockID.Hash, rs.ValidBlock.Hash())) 1000 } 1001 1002 // What we want: 1003 // P0 miss to lock B but set valid block to B after receiving delayed prevote. 1004 func TestSetValidBlockOnDelayedPrevote(t *testing.T) { 1005 cs1, vss := randState(4) 1006 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1007 height, round := cs1.Height, cs1.Round 1008 1009 partSize := types.BlockPartSizeBytes 1010 1011 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1012 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1013 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1014 validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) 1015 pv1, err := cs1.privValidator.GetPubKey() 1016 require.NoError(t, err) 1017 addr := pv1.Address() 1018 voteCh := subscribeToVoter(cs1, addr) 1019 1020 // start round and wait for propose and prevote 1021 startTestRound(cs1, cs1.Height, round) 1022 ensureNewRound(newRoundCh, height, round) 1023 1024 ensureNewProposal(proposalCh, height, round) 1025 rs := cs1.GetRoundState() 1026 propBlock := rs.ProposalBlock 1027 propBlockHash := propBlock.Hash() 1028 propBlockParts := propBlock.MakePartSet(partSize) 1029 1030 ensurePrevote(voteCh, height, round) 1031 validatePrevote(t, cs1, round, vss[0], propBlockHash) 1032 1033 // vs2 send prevote for propBlock 1034 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2) 1035 1036 // vs3 send prevote nil 1037 signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs3) 1038 1039 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) 1040 1041 ensurePrecommit(voteCh, height, round) 1042 // we should have precommitted 1043 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 1044 1045 rs = cs1.GetRoundState() 1046 1047 assert.True(t, rs.ValidBlock == nil) 1048 assert.True(t, rs.ValidBlockParts == nil) 1049 assert.True(t, rs.ValidRound == -1) 1050 1051 // vs2 send (delayed) prevote for propBlock 1052 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs4) 1053 1054 ensureNewValidBlock(validBlockCh, height, round) 1055 1056 rs = cs1.GetRoundState() 1057 1058 assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash)) 1059 assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header())) 1060 assert.True(t, rs.ValidRound == round) 1061 } 1062 1063 // What we want: 1064 // P0 miss to lock B as Proposal Block is missing, but set valid block to B after 1065 // receiving delayed Block Proposal. 1066 func TestSetValidBlockOnDelayedProposal(t *testing.T) { 1067 cs1, vss := randState(4) 1068 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1069 height, round := cs1.Height, cs1.Round 1070 1071 partSize := types.BlockPartSizeBytes 1072 1073 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1074 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 1075 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1076 validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) 1077 pv1, err := cs1.privValidator.GetPubKey() 1078 require.NoError(t, err) 1079 addr := pv1.Address() 1080 voteCh := subscribeToVoter(cs1, addr) 1081 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1082 1083 round++ // move to round in which P0 is not proposer 1084 incrementRound(vs2, vs3, vs4) 1085 1086 startTestRound(cs1, cs1.Height, round) 1087 ensureNewRound(newRoundCh, height, round) 1088 1089 ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) 1090 1091 ensurePrevote(voteCh, height, round) 1092 validatePrevote(t, cs1, round, vss[0], nil) 1093 1094 prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) 1095 propBlockHash := propBlock.Hash() 1096 propBlockParts := propBlock.MakePartSet(partSize) 1097 1098 // vs2, vs3 and vs4 send prevote for propBlock 1099 signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) 1100 ensureNewValidBlock(validBlockCh, height, round) 1101 1102 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) 1103 1104 ensurePrecommit(voteCh, height, round) 1105 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 1106 1107 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 1108 t.Fatal(err) 1109 } 1110 1111 ensureNewProposal(proposalCh, height, round) 1112 rs := cs1.GetRoundState() 1113 1114 assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash)) 1115 assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header())) 1116 assert.True(t, rs.ValidRound == round) 1117 } 1118 1119 // 4 vals, 3 Nil Precommits at P0 1120 // What we want: 1121 // P0 waits for timeoutPrecommit before starting next round 1122 func TestWaitingTimeoutOnNilPolka(t *testing.T) { 1123 cs1, vss := randState(4) 1124 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1125 height, round := cs1.Height, cs1.Round 1126 1127 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1128 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1129 1130 // start round 1131 startTestRound(cs1, height, round) 1132 ensureNewRound(newRoundCh, height, round) 1133 1134 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 1135 1136 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 1137 ensureNewRound(newRoundCh, height, round+1) 1138 } 1139 1140 // 4 vals, 3 Prevotes for nil from the higher round. 1141 // What we want: 1142 // P0 waits for timeoutPropose in the next round before entering prevote 1143 func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { 1144 cs1, vss := randState(4) 1145 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1146 height, round := cs1.Height, cs1.Round 1147 1148 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 1149 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1150 pv1, err := cs1.privValidator.GetPubKey() 1151 require.NoError(t, err) 1152 addr := pv1.Address() 1153 voteCh := subscribeToVoter(cs1, addr) 1154 1155 // start round 1156 startTestRound(cs1, height, round) 1157 ensureNewRound(newRoundCh, height, round) 1158 1159 ensurePrevote(voteCh, height, round) 1160 1161 incrementRound(vss[1:]...) 1162 signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 1163 1164 round++ // moving to the next round 1165 ensureNewRound(newRoundCh, height, round) 1166 1167 rs := cs1.GetRoundState() 1168 assert.True(t, rs.Step == cstypes.RoundStepPropose) // P0 does not prevote before timeoutPropose expires 1169 1170 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Propose(round).Nanoseconds()) 1171 1172 ensurePrevote(voteCh, height, round) 1173 validatePrevote(t, cs1, round, vss[0], nil) 1174 } 1175 1176 // 4 vals, 3 Precommits for nil from the higher round. 1177 // What we want: 1178 // P0 jump to higher round, precommit and start precommit wait 1179 func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { 1180 cs1, vss := randState(4) 1181 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1182 height, round := cs1.Height, cs1.Round 1183 1184 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1185 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1186 pv1, err := cs1.privValidator.GetPubKey() 1187 require.NoError(t, err) 1188 addr := pv1.Address() 1189 voteCh := subscribeToVoter(cs1, addr) 1190 1191 // start round 1192 startTestRound(cs1, height, round) 1193 ensureNewRound(newRoundCh, height, round) 1194 1195 ensurePrevote(voteCh, height, round) 1196 1197 incrementRound(vss[1:]...) 1198 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 1199 1200 round++ // moving to the next round 1201 ensureNewRound(newRoundCh, height, round) 1202 1203 ensurePrecommit(voteCh, height, round) 1204 validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) 1205 1206 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 1207 1208 round++ // moving to the next round 1209 ensureNewRound(newRoundCh, height, round) 1210 } 1211 1212 // 4 vals, 3 Prevotes for nil in the current round. 1213 // What we want: 1214 // P0 wait for timeoutPropose to expire before sending prevote. 1215 func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { 1216 cs1, vss := randState(4) 1217 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1218 height, round := cs1.Height, 1 1219 1220 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 1221 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1222 pv1, err := cs1.privValidator.GetPubKey() 1223 require.NoError(t, err) 1224 addr := pv1.Address() 1225 voteCh := subscribeToVoter(cs1, addr) 1226 1227 // start round in which PO is not proposer 1228 startTestRound(cs1, height, round) 1229 ensureNewRound(newRoundCh, height, round) 1230 1231 incrementRound(vss[1:]...) 1232 signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4) 1233 1234 ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) 1235 1236 ensurePrevote(voteCh, height, round) 1237 validatePrevote(t, cs1, round, vss[0], nil) 1238 } 1239 1240 // What we want: 1241 // P0 emit NewValidBlock event upon receiving 2/3+ Precommit for B but hasn't received block B yet 1242 func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) { 1243 cs1, vss := randState(4) 1244 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1245 height, round := cs1.Height, 1 1246 1247 incrementRound(vs2, vs3, vs4) 1248 1249 partSize := types.BlockPartSizeBytes 1250 1251 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1252 validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) 1253 1254 _, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round) 1255 propBlockHash := propBlock.Hash() 1256 propBlockParts := propBlock.MakePartSet(partSize) 1257 1258 // start round in which PO is not proposer 1259 startTestRound(cs1, height, round) 1260 ensureNewRound(newRoundCh, height, round) 1261 1262 // vs2, vs3 and vs4 send precommit for propBlock 1263 signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) 1264 ensureNewValidBlock(validBlockCh, height, round) 1265 1266 rs := cs1.GetRoundState() 1267 assert.True(t, rs.Step == cstypes.RoundStepCommit) 1268 assert.True(t, rs.ProposalBlock == nil) 1269 assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header())) 1270 1271 } 1272 1273 // What we want: 1274 // P0 receives 2/3+ Precommit for B for round 0, while being in round 1. It emits NewValidBlock event. 1275 // After receiving block, it executes block and moves to the next height. 1276 func TestCommitFromPreviousRound(t *testing.T) { 1277 cs1, vss := randState(4) 1278 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1279 height, round := cs1.Height, 1 1280 1281 partSize := types.BlockPartSizeBytes 1282 1283 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1284 validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) 1285 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1286 1287 prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round) 1288 propBlockHash := propBlock.Hash() 1289 propBlockParts := propBlock.MakePartSet(partSize) 1290 1291 // start round in which PO is not proposer 1292 startTestRound(cs1, height, round) 1293 ensureNewRound(newRoundCh, height, round) 1294 1295 // vs2, vs3 and vs4 send precommit for propBlock for the previous round 1296 signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) 1297 1298 ensureNewValidBlock(validBlockCh, height, round) 1299 1300 rs := cs1.GetRoundState() 1301 assert.True(t, rs.Step == cstypes.RoundStepCommit) 1302 assert.True(t, rs.CommitRound == vs2.Round) 1303 assert.True(t, rs.ProposalBlock == nil) 1304 assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header())) 1305 1306 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 1307 t.Fatal(err) 1308 } 1309 1310 ensureNewProposal(proposalCh, height, round) 1311 ensureNewRound(newRoundCh, height+1, 0) 1312 } 1313 1314 type fakeTxNotifier struct { 1315 ch chan struct{} 1316 } 1317 1318 func (n *fakeTxNotifier) TxsAvailable() <-chan struct{} { 1319 return n.ch 1320 } 1321 1322 func (n *fakeTxNotifier) Notify() { 1323 n.ch <- struct{}{} 1324 } 1325 1326 func TestStartNextHeightCorrectly(t *testing.T) { 1327 config.Consensus.SkipTimeoutCommit = false 1328 cs1, vss := randState(4) 1329 cs1.txNotifier = &fakeTxNotifier{ch: make(chan struct{})} 1330 1331 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1332 height, round := cs1.Height, cs1.Round 1333 1334 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1335 timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) 1336 1337 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1338 newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) 1339 pv1, err := cs1.privValidator.GetPubKey() 1340 require.NoError(t, err) 1341 addr := pv1.Address() 1342 voteCh := subscribeToVoter(cs1, addr) 1343 1344 // start round and wait for propose and prevote 1345 startTestRound(cs1, height, round) 1346 ensureNewRound(newRoundCh, height, round) 1347 1348 ensureNewProposal(proposalCh, height, round) 1349 rs := cs1.GetRoundState() 1350 theBlockHash := rs.ProposalBlock.Hash() 1351 theBlockParts := rs.ProposalBlockParts.Header() 1352 1353 ensurePrevote(voteCh, height, round) 1354 validatePrevote(t, cs1, round, vss[0], theBlockHash) 1355 1356 signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4) 1357 1358 ensurePrecommit(voteCh, height, round) 1359 // the proposed block should now be locked and our precommit added 1360 validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) 1361 1362 // add precommits 1363 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) 1364 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) 1365 time.Sleep(5 * time.Millisecond) 1366 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4) 1367 1368 rs = cs1.GetRoundState() 1369 assert.True(t, rs.TriggeredTimeoutPrecommit) 1370 1371 ensureNewBlockHeader(newBlockHeader, height, theBlockHash) 1372 1373 cs1.txNotifier.(*fakeTxNotifier).Notify() 1374 1375 ensureNewTimeout(timeoutProposeCh, height+1, round, cs1.config.Propose(round).Nanoseconds()) 1376 rs = cs1.GetRoundState() 1377 assert.False( 1378 t, 1379 rs.TriggeredTimeoutPrecommit, 1380 "triggeredTimeoutPrecommit should be false at the beginning of each round") 1381 } 1382 1383 func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) { 1384 config.Consensus.SkipTimeoutCommit = false 1385 cs1, vss := randState(4) 1386 1387 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1388 height, round := cs1.Height, cs1.Round 1389 1390 partSize := types.BlockPartSizeBytes 1391 1392 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1393 1394 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1395 newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) 1396 pv1, err := cs1.privValidator.GetPubKey() 1397 require.NoError(t, err) 1398 addr := pv1.Address() 1399 voteCh := subscribeToVoter(cs1, addr) 1400 1401 // start round and wait for propose and prevote 1402 startTestRound(cs1, height, round) 1403 ensureNewRound(newRoundCh, height, round) 1404 1405 ensureNewProposal(proposalCh, height, round) 1406 rs := cs1.GetRoundState() 1407 theBlockHash := rs.ProposalBlock.Hash() 1408 theBlockParts := rs.ProposalBlockParts.Header() 1409 1410 ensurePrevote(voteCh, height, round) 1411 validatePrevote(t, cs1, round, vss[0], theBlockHash) 1412 1413 signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4) 1414 1415 ensurePrecommit(voteCh, height, round) 1416 validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) 1417 1418 // add precommits 1419 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) 1420 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) 1421 signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4) 1422 1423 ensureNewBlockHeader(newBlockHeader, height, theBlockHash) 1424 1425 prop, propBlock := decideProposal(cs1, vs2, height+1, 0) 1426 propBlockParts := propBlock.MakePartSet(partSize) 1427 1428 if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { 1429 t.Fatal(err) 1430 } 1431 ensureNewProposal(proposalCh, height+1, 0) 1432 1433 rs = cs1.GetRoundState() 1434 assert.False( 1435 t, 1436 rs.TriggeredTimeoutPrecommit, 1437 "triggeredTimeoutPrecommit should be false at the beginning of each height") 1438 } 1439 1440 //------------------------------------------------------------------------------------------ 1441 // SlashingSuite 1442 // TODO: Slashing 1443 1444 /* 1445 func TestStateSlashingPrevotes(t *testing.T) { 1446 cs1, vss := randState(2) 1447 vs2 := vss[1] 1448 1449 1450 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1451 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1452 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1453 voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) 1454 1455 // start round and wait for propose and prevote 1456 startTestRound(cs1, cs1.Height, 0) 1457 <-newRoundCh 1458 re := <-proposalCh 1459 <-voteCh // prevote 1460 1461 rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState) 1462 1463 // we should now be stuck in limbo forever, waiting for more prevotes 1464 // add one for a different block should cause us to go into prevote wait 1465 hash := rs.ProposalBlock.Hash() 1466 hash[0] = byte(hash[0]+1) % 255 1467 signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlockParts.Header(), vs2) 1468 1469 <-timeoutWaitCh 1470 1471 // NOTE: we have to send the vote for different block first so we don't just go into precommit round right 1472 // away and ignore more prevotes (and thus fail to slash!) 1473 1474 // add the conflicting vote 1475 signAddVotes(cs1, types.PrevoteType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2) 1476 1477 // XXX: Check for existence of Dupeout info 1478 } 1479 1480 func TestStateSlashingPrecommits(t *testing.T) { 1481 cs1, vss := randState(2) 1482 vs2 := vss[1] 1483 1484 1485 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1486 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1487 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1488 voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) 1489 1490 // start round and wait for propose and prevote 1491 startTestRound(cs1, cs1.Height, 0) 1492 <-newRoundCh 1493 re := <-proposalCh 1494 <-voteCh // prevote 1495 1496 // add prevote from vs2 1497 signAddVotes(cs1, types.PrevoteType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2) 1498 1499 <-voteCh // precommit 1500 1501 // we should now be stuck in limbo forever, waiting for more prevotes 1502 // add one for a different block should cause us to go into prevote wait 1503 hash := rs.ProposalBlock.Hash() 1504 hash[0] = byte(hash[0]+1) % 255 1505 signAddVotes(cs1, types.PrecommitType, hash, rs.ProposalBlockParts.Header(), vs2) 1506 1507 // NOTE: we have to send the vote for different block first so we don't just go into precommit round right 1508 // away and ignore more prevotes (and thus fail to slash!) 1509 1510 // add precommit from vs2 1511 signAddVotes(cs1, types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2) 1512 1513 // XXX: Check for existence of Dupeout info 1514 } 1515 */ 1516 1517 //------------------------------------------------------------------------------------------ 1518 // CatchupSuite 1519 1520 //------------------------------------------------------------------------------------------ 1521 // HaltSuite 1522 1523 // 4 vals. 1524 // we receive a final precommit after going into next round, but others might have gone to commit already! 1525 func TestStateHalt1(t *testing.T) { 1526 cs1, vss := randState(4) 1527 vs2, vs3, vs4 := vss[1], vss[2], vss[3] 1528 height, round := cs1.Height, cs1.Round 1529 partSize := types.BlockPartSizeBytes 1530 1531 proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) 1532 timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) 1533 newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) 1534 newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock) 1535 pv1, err := cs1.privValidator.GetPubKey() 1536 require.NoError(t, err) 1537 addr := pv1.Address() 1538 voteCh := subscribeToVoter(cs1, addr) 1539 1540 // start round and wait for propose and prevote 1541 startTestRound(cs1, height, round) 1542 ensureNewRound(newRoundCh, height, round) 1543 1544 ensureNewProposal(proposalCh, height, round) 1545 rs := cs1.GetRoundState() 1546 propBlock := rs.ProposalBlock 1547 propBlockParts := propBlock.MakePartSet(partSize) 1548 1549 ensurePrevote(voteCh, height, round) 1550 1551 signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlockParts.Header(), vs2, vs3, vs4) 1552 1553 ensurePrecommit(voteCh, height, round) 1554 // the proposed block should now be locked and our precommit added 1555 validatePrecommit(t, cs1, round, round, vss[0], propBlock.Hash(), propBlock.Hash()) 1556 1557 // add precommits from the rest 1558 signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) // didnt receive proposal 1559 signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlockParts.Header(), vs3) 1560 // we receive this later, but vs3 might receive it earlier and with ours will go to commit! 1561 precommit4 := signVote(vs4, types.PrecommitType, propBlock.Hash(), propBlockParts.Header()) 1562 1563 incrementRound(vs2, vs3, vs4) 1564 1565 // timeout to new round 1566 ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) 1567 1568 round++ // moving to the next round 1569 1570 ensureNewRound(newRoundCh, height, round) 1571 rs = cs1.GetRoundState() 1572 1573 t.Log("### ONTO ROUND 1") 1574 /*Round2 1575 // we timeout and prevote our lock 1576 // a polka happened but we didn't see it! 1577 */ 1578 1579 // go to prevote, prevote for locked block 1580 ensurePrevote(voteCh, height, round) 1581 validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash()) 1582 1583 // now we receive the precommit from the previous round 1584 addVotes(cs1, precommit4) 1585 1586 // receiving that precommit should take us straight to commit 1587 ensureNewBlock(newBlockCh, height) 1588 1589 ensureNewRound(newRoundCh, height+1, 0) 1590 } 1591 1592 func TestStateOutputsBlockPartsStats(t *testing.T) { 1593 // create dummy peer 1594 cs, _ := randState(1) 1595 peer := p2pmock.NewPeer(nil) 1596 1597 // 1) new block part 1598 parts := types.NewPartSetFromData(tmrand.Bytes(100), 10) 1599 msg := &BlockPartMessage{ 1600 Height: 1, 1601 Round: 0, 1602 Part: parts.GetPart(0), 1603 } 1604 1605 cs.ProposalBlockParts = types.NewPartSetFromHeader(parts.Header()) 1606 cs.handleMsg(msgInfo{msg, peer.ID()}) 1607 1608 statsMessage := <-cs.statsMsgQueue 1609 require.Equal(t, msg, statsMessage.Msg, "") 1610 require.Equal(t, peer.ID(), statsMessage.PeerID, "") 1611 1612 // sending the same part from different peer 1613 cs.handleMsg(msgInfo{msg, "peer2"}) 1614 1615 // sending the part with the same height, but different round 1616 msg.Round = 1 1617 cs.handleMsg(msgInfo{msg, peer.ID()}) 1618 1619 // sending the part from the smaller height 1620 msg.Height = 0 1621 cs.handleMsg(msgInfo{msg, peer.ID()}) 1622 1623 // sending the part from the bigger height 1624 msg.Height = 3 1625 cs.handleMsg(msgInfo{msg, peer.ID()}) 1626 1627 select { 1628 case <-cs.statsMsgQueue: 1629 t.Errorf("should not output stats message after receiving the known block part!") 1630 case <-time.After(50 * time.Millisecond): 1631 } 1632 1633 } 1634 1635 func TestStateOutputVoteStats(t *testing.T) { 1636 cs, vss := randState(2) 1637 // create dummy peer 1638 peer := p2pmock.NewPeer(nil) 1639 1640 vote := signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{}) 1641 1642 voteMessage := &VoteMessage{vote} 1643 cs.handleMsg(msgInfo{voteMessage, peer.ID()}) 1644 1645 statsMessage := <-cs.statsMsgQueue 1646 require.Equal(t, voteMessage, statsMessage.Msg, "") 1647 require.Equal(t, peer.ID(), statsMessage.PeerID, "") 1648 1649 // sending the same part from different peer 1650 cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2"}) 1651 1652 // sending the vote for the bigger height 1653 incrementHeight(vss[1]) 1654 vote = signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{}) 1655 1656 cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()}) 1657 1658 select { 1659 case <-cs.statsMsgQueue: 1660 t.Errorf("should not output stats message after receiving the known vote or vote from bigger height") 1661 case <-time.After(50 * time.Millisecond): 1662 } 1663 1664 } 1665 1666 // subscribe subscribes test client to the given query and returns a channel with cap = 1. 1667 func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message { 1668 sub, err := eventBus.Subscribe(context.Background(), testSubscriber, q) 1669 if err != nil { 1670 panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q)) 1671 } 1672 return sub.Out() 1673 } 1674 1675 // subscribe subscribes test client to the given query and returns a channel with cap = 0. 1676 func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message { 1677 sub, err := eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, q) 1678 if err != nil { 1679 panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q)) 1680 } 1681 return sub.Out() 1682 }