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