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