You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1099 lines
35 KiB

  1. package consensus
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "testing"
  7. "time"
  8. cstypes "github.com/tendermint/tendermint/consensus/types"
  9. tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
  10. "github.com/tendermint/tendermint/types"
  11. cmn "github.com/tendermint/tmlibs/common"
  12. "github.com/tendermint/tmlibs/log"
  13. )
  14. func init() {
  15. config = ResetConfig("consensus_state_test")
  16. }
  17. func ensureProposeTimeout(timeoutPropose int) time.Duration {
  18. return time.Duration(timeoutPropose*2) * time.Millisecond
  19. }
  20. /*
  21. ProposeSuite
  22. x * TestProposerSelection0 - round robin ordering, round 0
  23. x * TestProposerSelection2 - round robin ordering, round 2++
  24. x * TestEnterProposeNoValidator - timeout into prevote round
  25. x * TestEnterPropose - finish propose without timing out (we have the proposal)
  26. x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevote and precommit nil
  27. FullRoundSuite
  28. x * TestFullRound1 - 1 val, full successful round
  29. x * TestFullRoundNil - 1 val, full round of nil
  30. x * TestFullRound2 - 2 vals, both required for full round
  31. LockSuite
  32. x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first.
  33. x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
  34. x * TestLockPOLUnlock - 4 vals, one precommits, other 3 polka nil at next round, so we unlock and precomit nil
  35. x * TestLockPOLSafety1 - 4 vals. We shouldn't change lock based on polka at earlier round
  36. x * TestLockPOLSafety2 - 4 vals. After unlocking, we shouldn't relock based on polka at earlier round
  37. * TestNetworkLock - once +1/3 precommits, network should be locked
  38. * TestNetworkLockPOL - once +1/3 precommits, the block with more recent polka is committed
  39. SlashingSuite
  40. x * TestSlashingPrevotes - a validator prevoting twice in a round gets slashed
  41. x * TestSlashingPrecommits - a validator precomitting twice in a round gets slashed
  42. CatchupSuite
  43. * TestCatchup - if we might be behind and we've seen any 2/3 prevotes, round skip to new round, precommit, or prevote
  44. HaltSuite
  45. x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we should still commit
  46. */
  47. //----------------------------------------------------------------------------------------------------
  48. // ProposeSuite
  49. func TestStateProposerSelection0(t *testing.T) {
  50. cs1, vss := randConsensusState(4)
  51. height, round := cs1.Height, cs1.Round
  52. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  53. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  54. startTestRound(cs1, height, round)
  55. // wait for new round so proposer is set
  56. <-newRoundCh
  57. // lets commit a block and ensure proposer for the next height is correct
  58. prop := cs1.GetRoundState().Validators.GetProposer()
  59. if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
  60. t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
  61. }
  62. // wait for complete proposal
  63. <-proposalCh
  64. rs := cs1.GetRoundState()
  65. signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
  66. // wait for new round so next validator is set
  67. <-newRoundCh
  68. prop = cs1.GetRoundState().Validators.GetProposer()
  69. if !bytes.Equal(prop.Address, vss[1].GetAddress()) {
  70. panic(cmn.Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address))
  71. }
  72. }
  73. // Now let's do it all again, but starting from round 2 instead of 0
  74. func TestStateProposerSelection2(t *testing.T) {
  75. cs1, vss := randConsensusState(4) // test needs more work for more than 3 validators
  76. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  77. // this time we jump in at round 2
  78. incrementRound(vss[1:]...)
  79. incrementRound(vss[1:]...)
  80. startTestRound(cs1, cs1.Height, 2)
  81. <-newRoundCh // wait for the new round
  82. // everyone just votes nil. we get a new proposer each round
  83. for i := 0; i < len(vss); i++ {
  84. prop := cs1.GetRoundState().Validators.GetProposer()
  85. if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].GetAddress()) {
  86. panic(cmn.Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
  87. }
  88. rs := cs1.GetRoundState()
  89. signAddVotes(cs1, types.VoteTypePrecommit, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
  90. <-newRoundCh // wait for the new round event each round
  91. incrementRound(vss[1:]...)
  92. }
  93. }
  94. // a non-validator should timeout into the prevote round
  95. func TestStateEnterProposeNoPrivValidator(t *testing.T) {
  96. cs, _ := randConsensusState(1)
  97. cs.SetPrivValidator(nil)
  98. height, round := cs.Height, cs.Round
  99. // Listen for propose timeout event
  100. timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
  101. startTestRound(cs, height, round)
  102. // if we're not a validator, EnterPropose should timeout
  103. ticker := time.NewTicker(ensureProposeTimeout(cs.config.TimeoutPropose))
  104. select {
  105. case <-timeoutCh:
  106. case <-ticker.C:
  107. panic("Expected EnterPropose to timeout")
  108. }
  109. if cs.GetRoundState().Proposal != nil {
  110. t.Error("Expected to make no proposal, since no privValidator")
  111. }
  112. }
  113. // a validator should not timeout of the prevote round (TODO: unless the block is really big!)
  114. func TestStateEnterProposeYesPrivValidator(t *testing.T) {
  115. cs, _ := randConsensusState(1)
  116. height, round := cs.Height, cs.Round
  117. // Listen for propose timeout event
  118. timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
  119. proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
  120. cs.enterNewRound(height, round)
  121. cs.startRoutines(3)
  122. <-proposalCh
  123. // Check that Proposal, ProposalBlock, ProposalBlockParts are set.
  124. rs := cs.GetRoundState()
  125. if rs.Proposal == nil {
  126. t.Error("rs.Proposal should be set")
  127. }
  128. if rs.ProposalBlock == nil {
  129. t.Error("rs.ProposalBlock should be set")
  130. }
  131. if rs.ProposalBlockParts.Total() == 0 {
  132. t.Error("rs.ProposalBlockParts should be set")
  133. }
  134. // if we're a validator, enterPropose should not timeout
  135. ticker := time.NewTicker(ensureProposeTimeout(cs.config.TimeoutPropose))
  136. select {
  137. case <-timeoutCh:
  138. panic("Expected EnterPropose not to timeout")
  139. case <-ticker.C:
  140. }
  141. }
  142. func TestStateBadProposal(t *testing.T) {
  143. cs1, vss := randConsensusState(2)
  144. height, round := cs1.Height, cs1.Round
  145. vs2 := vss[1]
  146. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  147. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  148. voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
  149. propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2)
  150. // make the second validator the proposer by incrementing round
  151. round = round + 1
  152. incrementRound(vss[1:]...)
  153. // make the block bad by tampering with statehash
  154. stateHash := propBlock.AppHash
  155. if len(stateHash) == 0 {
  156. stateHash = make([]byte, 32)
  157. }
  158. stateHash[0] = byte((stateHash[0] + 1) % 255)
  159. propBlock.AppHash = stateHash
  160. propBlockParts := propBlock.MakePartSet(partSize)
  161. proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1, types.BlockID{})
  162. if err := vs2.SignProposal(config.ChainID(), proposal); err != nil {
  163. t.Fatal("failed to sign bad proposal", err)
  164. }
  165. // set the proposal block
  166. if err := cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil {
  167. t.Fatal(err)
  168. }
  169. // start the machine
  170. startTestRound(cs1, height, round)
  171. // wait for proposal
  172. <-proposalCh
  173. // wait for prevote
  174. <-voteCh
  175. validatePrevote(t, cs1, round, vss[0], nil)
  176. // add bad prevote from vs2 and wait for it
  177. signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
  178. <-voteCh
  179. // wait for precommit
  180. <-voteCh
  181. validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
  182. signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
  183. }
  184. //----------------------------------------------------------------------------------------------------
  185. // FullRoundSuite
  186. // propose, prevote, and precommit a block
  187. func TestStateFullRound1(t *testing.T) {
  188. cs, vss := randConsensusState(1)
  189. height, round := cs.Height, cs.Round
  190. // NOTE: buffer capacity of 0 ensures we can validate prevote and last commit
  191. // before consensus can move to the next height (and cause a race condition)
  192. cs.eventBus.Stop()
  193. eventBus := types.NewEventBusWithBufferCapacity(0)
  194. eventBus.SetLogger(log.TestingLogger().With("module", "events"))
  195. cs.SetEventBus(eventBus)
  196. eventBus.Start()
  197. voteCh := subscribe(cs.eventBus, types.EventQueryVote)
  198. propCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
  199. newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound)
  200. startTestRound(cs, height, round)
  201. <-newRoundCh
  202. // grab proposal
  203. re := <-propCh
  204. propBlockHash := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash()
  205. <-voteCh // wait for prevote
  206. validatePrevote(t, cs, round, vss[0], propBlockHash)
  207. <-voteCh // wait for precommit
  208. // we're going to roll right into new height
  209. <-newRoundCh
  210. validateLastPrecommit(t, cs, vss[0], propBlockHash)
  211. }
  212. // nil is proposed, so prevote and precommit nil
  213. func TestStateFullRoundNil(t *testing.T) {
  214. cs, vss := randConsensusState(1)
  215. height, round := cs.Height, cs.Round
  216. voteCh := subscribe(cs.eventBus, types.EventQueryVote)
  217. cs.enterPrevote(height, round)
  218. cs.startRoutines(4)
  219. <-voteCh // prevote
  220. <-voteCh // precommit
  221. // should prevote and precommit nil
  222. validatePrevoteAndPrecommit(t, cs, round, 0, vss[0], nil, nil)
  223. }
  224. // run through propose, prevote, precommit commit with two validators
  225. // where the first validator has to wait for votes from the second
  226. func TestStateFullRound2(t *testing.T) {
  227. cs1, vss := randConsensusState(2)
  228. vs2 := vss[1]
  229. height, round := cs1.Height, cs1.Round
  230. voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
  231. newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
  232. // start round and wait for propose and prevote
  233. startTestRound(cs1, height, round)
  234. <-voteCh // prevote
  235. // we should be stuck in limbo waiting for more prevotes
  236. rs := cs1.GetRoundState()
  237. propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
  238. // prevote arrives from vs2:
  239. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propPartsHeader, vs2)
  240. <-voteCh
  241. <-voteCh //precommit
  242. // the proposed block should now be locked and our precommit added
  243. validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash)
  244. // we should be stuck in limbo waiting for more precommits
  245. // precommit arrives from vs2:
  246. signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propPartsHeader, vs2)
  247. <-voteCh
  248. // wait to finish commit, propose in next height
  249. <-newBlockCh
  250. }
  251. //------------------------------------------------------------------------------------------
  252. // LockSuite
  253. // two validators, 4 rounds.
  254. // two vals take turns proposing. val1 locks on first one, precommits nil on everything else
  255. func TestStateLockNoPOL(t *testing.T) {
  256. cs1, vss := randConsensusState(2)
  257. vs2 := vss[1]
  258. height := cs1.Height
  259. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  260. timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  261. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  262. voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
  263. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  264. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  265. /*
  266. Round1 (cs1, B) // B B // B B2
  267. */
  268. // start round and wait for prevote
  269. cs1.enterNewRound(height, 0)
  270. cs1.startRoutines(0)
  271. re := <-proposalCh
  272. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  273. theBlockHash := rs.ProposalBlock.Hash()
  274. <-voteCh // prevote
  275. // we should now be stuck in limbo forever, waiting for more prevotes
  276. // prevote arrives from vs2:
  277. signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2)
  278. <-voteCh // prevote
  279. <-voteCh // precommit
  280. // the proposed block should now be locked and our precommit added
  281. validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
  282. // we should now be stuck in limbo forever, waiting for more precommits
  283. // lets add one for a different block
  284. // NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
  285. hash := make([]byte, len(theBlockHash))
  286. copy(hash, theBlockHash)
  287. hash[0] = byte((hash[0] + 1) % 255)
  288. signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
  289. <-voteCh // precommit
  290. // (note we're entering precommit for a second time this round)
  291. // but with invalid args. then we enterPrecommitWait, and the timeout to new round
  292. <-timeoutWaitCh
  293. ///
  294. <-newRoundCh
  295. t.Log("#### ONTO ROUND 1")
  296. /*
  297. Round2 (cs1, B) // B B2
  298. */
  299. incrementRound(vs2)
  300. // now we're on a new round and not the proposer, so wait for timeout
  301. re = <-timeoutProposeCh
  302. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  303. if rs.ProposalBlock != nil {
  304. panic("Expected proposal block to be nil")
  305. }
  306. // wait to finish prevote
  307. <-voteCh
  308. // we should have prevoted our locked block
  309. validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
  310. // add a conflicting prevote from the other validator
  311. signAddVotes(cs1, types.VoteTypePrevote, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
  312. <-voteCh
  313. // now we're going to enter prevote again, but with invalid args
  314. // and then prevote wait, which should timeout. then wait for precommit
  315. <-timeoutWaitCh
  316. <-voteCh // precommit
  317. // the proposed block should still be locked and our precommit added
  318. // we should precommit nil and be locked on the proposal
  319. validatePrecommit(t, cs1, 1, 0, vss[0], nil, theBlockHash)
  320. // add conflicting precommit from vs2
  321. // NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
  322. signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
  323. <-voteCh
  324. // (note we're entering precommit for a second time this round, but with invalid args
  325. // then we enterPrecommitWait and timeout into NewRound
  326. <-timeoutWaitCh
  327. <-newRoundCh
  328. t.Log("#### ONTO ROUND 2")
  329. /*
  330. Round3 (vs2, _) // B, B2
  331. */
  332. incrementRound(vs2)
  333. re = <-proposalCh
  334. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  335. // now we're on a new round and are the proposer
  336. if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
  337. panic(cmn.Fmt("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock))
  338. }
  339. <-voteCh // prevote
  340. validatePrevote(t, cs1, 2, vss[0], rs.LockedBlock.Hash())
  341. signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
  342. <-voteCh
  343. <-timeoutWaitCh // prevote wait
  344. <-voteCh // precommit
  345. validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
  346. signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
  347. <-voteCh
  348. <-timeoutWaitCh
  349. // before we time out into new round, set next proposal block
  350. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  351. if prop == nil || propBlock == nil {
  352. t.Fatal("Failed to create proposal block with vs2")
  353. }
  354. incrementRound(vs2)
  355. <-newRoundCh
  356. t.Log("#### ONTO ROUND 3")
  357. /*
  358. Round4 (vs2, C) // B C // B C
  359. */
  360. // now we're on a new round and not the proposer
  361. // so set the proposal block
  362. if err := cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(partSize), ""); err != nil {
  363. t.Fatal(err)
  364. }
  365. <-proposalCh
  366. <-voteCh // prevote
  367. // prevote for locked block (not proposal)
  368. validatePrevote(t, cs1, 0, vss[0], cs1.LockedBlock.Hash())
  369. signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
  370. <-voteCh
  371. <-timeoutWaitCh
  372. <-voteCh
  373. validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
  374. signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
  375. <-voteCh
  376. }
  377. // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
  378. func TestStateLockPOLRelock(t *testing.T) {
  379. cs1, vss := randConsensusState(4)
  380. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  381. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  382. timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  383. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  384. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  385. voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
  386. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  387. newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
  388. // everything done from perspective of cs1
  389. /*
  390. Round1 (cs1, B) // B B B B// B nil B nil
  391. eg. vs2 and vs4 didn't see the 2/3 prevotes
  392. */
  393. // start round and wait for propose and prevote
  394. startTestRound(cs1, cs1.Height, 0)
  395. <-newRoundCh
  396. re := <-proposalCh
  397. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  398. theBlockHash := rs.ProposalBlock.Hash()
  399. <-voteCh // prevote
  400. signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
  401. // prevotes
  402. discardFromChan(voteCh, 3)
  403. <-voteCh // our precommit
  404. // the proposed block should now be locked and our precommit added
  405. validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
  406. // add precommits from the rest
  407. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
  408. signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
  409. // precommites
  410. discardFromChan(voteCh, 3)
  411. // before we timeout to the new round set the new proposal
  412. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  413. propBlockParts := propBlock.MakePartSet(partSize)
  414. propBlockHash := propBlock.Hash()
  415. incrementRound(vs2, vs3, vs4)
  416. // timeout to new round
  417. <-timeoutWaitCh
  418. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  419. if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  420. t.Fatal(err)
  421. }
  422. <-newRoundCh
  423. t.Log("### ONTO ROUND 1")
  424. /*
  425. Round2 (vs2, C) // B C C C // C C C _)
  426. cs1 changes lock!
  427. */
  428. // now we're on a new round and not the proposer
  429. // but we should receive the proposal
  430. select {
  431. case <-proposalCh:
  432. case <-timeoutProposeCh:
  433. <-proposalCh
  434. }
  435. // go to prevote, prevote for locked block (not proposal), move on
  436. <-voteCh
  437. validatePrevote(t, cs1, 0, vss[0], theBlockHash)
  438. // now lets add prevotes from everyone else for the new block
  439. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  440. // prevotes
  441. discardFromChan(voteCh, 3)
  442. // now either we go to PrevoteWait or Precommit
  443. select {
  444. case <-timeoutWaitCh: // we're in PrevoteWait, go to Precommit
  445. // XXX: there's no guarantee we see the polka, this might be a precommit for nil,
  446. // in which case the test fails!
  447. <-voteCh
  448. case <-voteCh: // we went straight to Precommit
  449. }
  450. // we should have unlocked and locked on the new block
  451. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
  452. signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propBlockParts.Header(), vs2, vs3)
  453. discardFromChan(voteCh, 2)
  454. be := <-newBlockCh
  455. b := be.(types.EventDataNewBlockHeader)
  456. re = <-newRoundCh
  457. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  458. if rs.Height != 2 {
  459. panic("Expected height to increment")
  460. }
  461. if !bytes.Equal(b.Header.Hash(), propBlockHash) {
  462. panic("Expected new block to be proposal block")
  463. }
  464. }
  465. // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
  466. func TestStateLockPOLUnlock(t *testing.T) {
  467. cs1, vss := randConsensusState(4)
  468. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  469. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  470. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  471. timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  472. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  473. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  474. unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
  475. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  476. // everything done from perspective of cs1
  477. /*
  478. Round1 (cs1, B) // B B B B // B nil B nil
  479. eg. didn't see the 2/3 prevotes
  480. */
  481. // start round and wait for propose and prevote
  482. startTestRound(cs1, cs1.Height, 0)
  483. <-newRoundCh
  484. re := <-proposalCh
  485. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  486. theBlockHash := rs.ProposalBlock.Hash()
  487. <-voteCh // prevote
  488. signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
  489. <-voteCh //precommit
  490. // the proposed block should now be locked and our precommit added
  491. validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
  492. rs = cs1.GetRoundState()
  493. // add precommits from the rest
  494. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
  495. signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
  496. // before we time out into new round, set next proposal block
  497. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  498. propBlockParts := propBlock.MakePartSet(partSize)
  499. incrementRound(vs2, vs3, vs4)
  500. // timeout to new round
  501. re = <-timeoutWaitCh
  502. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  503. lockedBlockHash := rs.LockedBlock.Hash()
  504. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  505. if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  506. t.Fatal(err)
  507. }
  508. <-newRoundCh
  509. t.Log("#### ONTO ROUND 1")
  510. /*
  511. Round2 (vs2, C) // B nil nil nil // nil nil nil _
  512. cs1 unlocks!
  513. */
  514. // now we're on a new round and not the proposer,
  515. // but we should receive the proposal
  516. select {
  517. case <-proposalCh:
  518. case <-timeoutProposeCh:
  519. <-proposalCh
  520. }
  521. // go to prevote, prevote for locked block (not proposal)
  522. <-voteCh
  523. validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
  524. // now lets add prevotes from everyone else for nil (a polka!)
  525. signAddVotes(cs1, types.VoteTypePrevote, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  526. // the polka makes us unlock and precommit nil
  527. <-unlockCh
  528. <-voteCh // precommit
  529. // we should have unlocked and committed nil
  530. // NOTE: since we don't relock on nil, the lock round is 0
  531. validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
  532. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
  533. <-newRoundCh
  534. }
  535. // 4 vals
  536. // a polka at round 1 but we miss it
  537. // then a polka at round 2 that we lock on
  538. // then we see the polka from round 1 but shouldn't unlock
  539. func TestStateLockPOLSafety1(t *testing.T) {
  540. cs1, vss := randConsensusState(4)
  541. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  542. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  543. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  544. timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  545. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  546. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  547. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  548. // start round and wait for propose and prevote
  549. startTestRound(cs1, cs1.Height, 0)
  550. <-newRoundCh
  551. re := <-proposalCh
  552. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  553. propBlock := rs.ProposalBlock
  554. <-voteCh // prevote
  555. validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
  556. // the others sign a polka but we don't see it
  557. prevotes := signVotes(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
  558. // before we time out into new round, set next proposer
  559. // and next proposal block
  560. /*
  561. _, v1 := cs1.Validators.GetByAddress(vss[0].Address)
  562. v1.VotingPower = 1
  563. if updated := cs1.Validators.Update(v1); !updated {
  564. panic("failed to update validator")
  565. }*/
  566. t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash()))
  567. // we do see them precommit nil
  568. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  569. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  570. propBlockHash := propBlock.Hash()
  571. propBlockParts := propBlock.MakePartSet(partSize)
  572. incrementRound(vs2, vs3, vs4)
  573. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  574. if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
  575. t.Fatal(err)
  576. }
  577. <-newRoundCh
  578. t.Log("### ONTO ROUND 1")
  579. /*Round2
  580. // we timeout and prevote our lock
  581. // a polka happened but we didn't see it!
  582. */
  583. // now we're on a new round and not the proposer,
  584. // but we should receive the proposal
  585. select {
  586. case re = <-proposalCh:
  587. case <-timeoutProposeCh:
  588. re = <-proposalCh
  589. }
  590. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  591. if rs.LockedBlock != nil {
  592. panic("we should not be locked!")
  593. }
  594. t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash))
  595. // go to prevote, prevote for proposal block
  596. <-voteCh
  597. validatePrevote(t, cs1, 1, vss[0], propBlockHash)
  598. // now we see the others prevote for it, so we should lock on it
  599. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  600. <-voteCh // precommit
  601. // we should have precommitted
  602. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
  603. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
  604. <-timeoutWaitCh
  605. incrementRound(vs2, vs3, vs4)
  606. <-newRoundCh
  607. t.Log("### ONTO ROUND 2")
  608. /*Round3
  609. we see the polka from round 1 but we shouldn't unlock!
  610. */
  611. // timeout of propose
  612. <-timeoutProposeCh
  613. // finish prevote
  614. <-voteCh
  615. // we should prevote what we're locked on
  616. validatePrevote(t, cs1, 2, vss[0], propBlockHash)
  617. newStepCh := subscribe(cs1.eventBus, types.EventQueryNewRoundStep)
  618. // add prevotes from the earlier round
  619. addVotes(cs1, prevotes...)
  620. t.Log("Done adding prevotes!")
  621. ensureNoNewStep(newStepCh)
  622. }
  623. // 4 vals.
  624. // polka P0 at R0, P1 at R1, and P2 at R2,
  625. // we lock on P0 at R0, don't see P1, and unlock using P2 at R2
  626. // then we should make sure we don't lock using P1
  627. // What we want:
  628. // dont see P0, lock on P1 at R1, dont unlock using P0 at R2
  629. func TestStateLockPOLSafety2(t *testing.T) {
  630. cs1, vss := randConsensusState(4)
  631. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  632. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  633. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  634. timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
  635. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  636. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  637. unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
  638. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  639. // the block for R0: gets polkad but we miss it
  640. // (even though we signed it, shhh)
  641. _, propBlock0 := decideProposal(cs1, vss[0], cs1.Height, cs1.Round)
  642. propBlockHash0 := propBlock0.Hash()
  643. propBlockParts0 := propBlock0.MakePartSet(partSize)
  644. // the others sign a polka but we don't see it
  645. prevotes := signVotes(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
  646. // the block for round 1
  647. prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  648. propBlockHash1 := propBlock1.Hash()
  649. propBlockParts1 := propBlock1.MakePartSet(partSize)
  650. propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()}
  651. incrementRound(vs2, vs3, vs4)
  652. cs1.updateRoundStep(0, cstypes.RoundStepPrecommitWait)
  653. t.Log("### ONTO Round 1")
  654. // jump in at round 1
  655. height := cs1.Height
  656. startTestRound(cs1, height, 1)
  657. <-newRoundCh
  658. if err := cs1.SetProposalAndBlock(prop1, propBlock1, propBlockParts1, "some peer"); err != nil {
  659. t.Fatal(err)
  660. }
  661. <-proposalCh
  662. <-voteCh // prevote
  663. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
  664. <-voteCh // precommit
  665. // the proposed block should now be locked and our precommit added
  666. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
  667. // add precommits from the rest
  668. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
  669. signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash1, propBlockParts1.Header(), vs3)
  670. incrementRound(vs2, vs3, vs4)
  671. // timeout of precommit wait to new round
  672. <-timeoutWaitCh
  673. // in round 2 we see the polkad block from round 0
  674. newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0, propBlockID1)
  675. if err := vs3.SignProposal(config.ChainID(), newProp); err != nil {
  676. t.Fatal(err)
  677. }
  678. if err := cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer"); err != nil {
  679. t.Fatal(err)
  680. }
  681. // Add the pol votes
  682. addVotes(cs1, prevotes...)
  683. <-newRoundCh
  684. t.Log("### ONTO Round 2")
  685. /*Round2
  686. // now we see the polka from round 1, but we shouldnt unlock
  687. */
  688. select {
  689. case <-timeoutProposeCh:
  690. <-proposalCh
  691. case <-proposalCh:
  692. }
  693. select {
  694. case <-unlockCh:
  695. panic("validator unlocked using an old polka")
  696. case <-voteCh:
  697. // prevote our locked block
  698. }
  699. validatePrevote(t, cs1, 2, vss[0], propBlockHash1)
  700. }
  701. //------------------------------------------------------------------------------------------
  702. // SlashingSuite
  703. // TODO: Slashing
  704. /*
  705. func TestStateSlashingPrevotes(t *testing.T) {
  706. cs1, vss := randConsensusState(2)
  707. vs2 := vss[1]
  708. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  709. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  710. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  711. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  712. // start round and wait for propose and prevote
  713. startTestRound(cs1, cs1.Height, 0)
  714. <-newRoundCh
  715. re := <-proposalCh
  716. <-voteCh // prevote
  717. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  718. // we should now be stuck in limbo forever, waiting for more prevotes
  719. // add one for a different block should cause us to go into prevote wait
  720. hash := rs.ProposalBlock.Hash()
  721. hash[0] = byte(hash[0]+1) % 255
  722. signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlockParts.Header(), vs2)
  723. <-timeoutWaitCh
  724. // NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  725. // away and ignore more prevotes (and thus fail to slash!)
  726. // add the conflicting vote
  727. signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  728. // XXX: Check for existence of Dupeout info
  729. }
  730. func TestStateSlashingPrecommits(t *testing.T) {
  731. cs1, vss := randConsensusState(2)
  732. vs2 := vss[1]
  733. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  734. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  735. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  736. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  737. // start round and wait for propose and prevote
  738. startTestRound(cs1, cs1.Height, 0)
  739. <-newRoundCh
  740. re := <-proposalCh
  741. <-voteCh // prevote
  742. // add prevote from vs2
  743. signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  744. <-voteCh // precommit
  745. // we should now be stuck in limbo forever, waiting for more prevotes
  746. // add one for a different block should cause us to go into prevote wait
  747. hash := rs.ProposalBlock.Hash()
  748. hash[0] = byte(hash[0]+1) % 255
  749. signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlockParts.Header(), vs2)
  750. // NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  751. // away and ignore more prevotes (and thus fail to slash!)
  752. // add precommit from vs2
  753. signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  754. // XXX: Check for existence of Dupeout info
  755. }
  756. */
  757. //------------------------------------------------------------------------------------------
  758. // CatchupSuite
  759. //------------------------------------------------------------------------------------------
  760. // HaltSuite
  761. // 4 vals.
  762. // we receive a final precommit after going into next round, but others might have gone to commit already!
  763. func TestStateHalt1(t *testing.T) {
  764. cs1, vss := randConsensusState(4)
  765. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  766. partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
  767. proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
  768. timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
  769. newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
  770. newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
  771. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  772. // start round and wait for propose and prevote
  773. startTestRound(cs1, cs1.Height, 0)
  774. <-newRoundCh
  775. re := <-proposalCh
  776. rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  777. propBlock := rs.ProposalBlock
  778. propBlockParts := propBlock.MakePartSet(partSize)
  779. <-voteCh // prevote
  780. signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlockParts.Header(), vs3, vs4)
  781. <-voteCh // precommit
  782. // the proposed block should now be locked and our precommit added
  783. validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
  784. // add precommits from the rest
  785. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
  786. signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header(), vs3)
  787. // we receive this later, but vs3 might receive it earlier and with ours will go to commit!
  788. precommit4 := signVote(vs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
  789. incrementRound(vs2, vs3, vs4)
  790. // timeout to new round
  791. <-timeoutWaitCh
  792. re = <-newRoundCh
  793. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  794. t.Log("### ONTO ROUND 1")
  795. /*Round2
  796. // we timeout and prevote our lock
  797. // a polka happened but we didn't see it!
  798. */
  799. // go to prevote, prevote for locked block
  800. <-voteCh // prevote
  801. validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
  802. // now we receive the precommit from the previous round
  803. addVotes(cs1, precommit4)
  804. // receiving that precommit should take us straight to commit
  805. <-newBlockCh
  806. re = <-newRoundCh
  807. rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
  808. if rs.Height != 2 {
  809. panic("expected height to increment")
  810. }
  811. }
  812. // subscribe subscribes test client to the given query and returns a channel with cap = 1.
  813. func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan interface{} {
  814. out := make(chan interface{}, 1)
  815. err := eventBus.Subscribe(context.Background(), testSubscriber, q, out)
  816. if err != nil {
  817. panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q))
  818. }
  819. return out
  820. }
  821. // discardFromChan reads n values from the channel.
  822. func discardFromChan(ch <-chan interface{}, n int) {
  823. for i := 0; i < n; i++ {
  824. <-ch
  825. }
  826. }