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.

1058 lines
34 KiB

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