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