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.

1057 lines
35 KiB

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