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