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