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.

1060 lines
34 KiB

9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 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. t.Log("#### 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. t.Log("#### 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. t.Log("#### 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. t.Logf("vs2 last round %v", 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. // prevotes
  390. discardFromChan(voteCh, 3)
  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. // precommites
  398. discardFromChan(voteCh, 3)
  399. // before we timeout to the new round set the new proposal
  400. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  401. propBlockParts := propBlock.MakePartSet(partSize)
  402. propBlockHash := propBlock.Hash()
  403. incrementRound(vs2, vs3, vs4)
  404. // timeout to new round
  405. <-timeoutWaitCh
  406. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  407. cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
  408. <-newRoundCh
  409. t.Log("### ONTO ROUND 1")
  410. /*
  411. Round2 (vs2, C) // B C C C // C C C _)
  412. cs1 changes lock!
  413. */
  414. // now we're on a new round and not the proposer
  415. // but we should receive the proposal
  416. select {
  417. case <-proposalCh:
  418. case <-timeoutProposeCh:
  419. <-proposalCh
  420. }
  421. // go to prevote, prevote for locked block (not proposal), move on
  422. <-voteCh
  423. validatePrevote(t, cs1, 0, vss[0], theBlockHash)
  424. // now lets add prevotes from everyone else for the new block
  425. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  426. // prevotes
  427. discardFromChan(voteCh, 3)
  428. // now either we go to PrevoteWait or Precommit
  429. select {
  430. case <-timeoutWaitCh: // we're in PrevoteWait, go to Precommit
  431. // XXX: there's no guarantee we see the polka, this might be a precommit for nil,
  432. // in which case the test fails!
  433. <-voteCh
  434. case <-voteCh: // we went straight to Precommit
  435. }
  436. // we should have unlocked and locked on the new block
  437. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
  438. signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propBlockParts.Header(), vs2, vs3)
  439. discardFromChan(voteCh, 2)
  440. be := <-newBlockCh
  441. b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader)
  442. re = <-newRoundCh
  443. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  444. if rs.Height != 2 {
  445. panic("Expected height to increment")
  446. }
  447. if !bytes.Equal(b.Header.Hash(), propBlockHash) {
  448. panic("Expected new block to be proposal block")
  449. }
  450. }
  451. // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
  452. func TestLockPOLUnlock(t *testing.T) {
  453. cs1, vss := randConsensusState(4)
  454. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  455. partSize := config.Consensus.BlockPartSize
  456. proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
  457. timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
  458. timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
  459. newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
  460. unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
  461. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  462. // everything done from perspective of cs1
  463. /*
  464. Round1 (cs1, B) // B B B B // B nil B nil
  465. eg. didn't see the 2/3 prevotes
  466. */
  467. // start round and wait for propose and prevote
  468. startTestRound(cs1, cs1.Height, 0)
  469. <-newRoundCh
  470. re := <-proposalCh
  471. rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  472. theBlockHash := rs.ProposalBlock.Hash()
  473. <-voteCh // prevote
  474. signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
  475. <-voteCh //precommit
  476. // the proposed block should now be locked and our precommit added
  477. validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
  478. rs = cs1.GetRoundState()
  479. // add precommits from the rest
  480. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
  481. signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
  482. // before we time out into new round, set next proposal block
  483. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  484. propBlockParts := propBlock.MakePartSet(partSize)
  485. incrementRound(vs2, vs3, vs4)
  486. // timeout to new round
  487. re = <-timeoutWaitCh
  488. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  489. lockedBlockHash := rs.LockedBlock.Hash()
  490. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  491. cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
  492. <-newRoundCh
  493. t.Log("#### ONTO ROUND 1")
  494. /*
  495. Round2 (vs2, C) // B nil nil nil // nil nil nil _
  496. cs1 unlocks!
  497. */
  498. // now we're on a new round and not the proposer,
  499. // but we should receive the proposal
  500. select {
  501. case <-proposalCh:
  502. case <-timeoutProposeCh:
  503. <-proposalCh
  504. }
  505. // go to prevote, prevote for locked block (not proposal)
  506. <-voteCh
  507. validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
  508. // now lets add prevotes from everyone else for nil (a polka!)
  509. signAddVotes(cs1, types.VoteTypePrevote, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  510. // the polka makes us unlock and precommit nil
  511. <-unlockCh
  512. <-voteCh // precommit
  513. // we should have unlocked and committed nil
  514. // NOTE: since we don't relock on nil, the lock round is 0
  515. validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
  516. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
  517. <-newRoundCh
  518. }
  519. // 4 vals
  520. // a polka at round 1 but we miss it
  521. // then a polka at round 2 that we lock on
  522. // then we see the polka from round 1 but shouldn't unlock
  523. func TestLockPOLSafety1(t *testing.T) {
  524. cs1, vss := randConsensusState(4)
  525. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  526. partSize := config.Consensus.BlockPartSize
  527. proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
  528. timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
  529. timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
  530. newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
  531. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  532. // start round and wait for propose and prevote
  533. startTestRound(cs1, cs1.Height, 0)
  534. <-newRoundCh
  535. re := <-proposalCh
  536. rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  537. propBlock := rs.ProposalBlock
  538. <-voteCh // prevote
  539. validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
  540. // the others sign a polka but we don't see it
  541. prevotes := signVotes(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
  542. // before we time out into new round, set next proposer
  543. // and next proposal block
  544. /*
  545. _, v1 := cs1.Validators.GetByAddress(vss[0].Address)
  546. v1.VotingPower = 1
  547. if updated := cs1.Validators.Update(v1); !updated {
  548. panic("failed to update validator")
  549. }*/
  550. t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash()))
  551. // we do see them precommit nil
  552. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3, vs4)
  553. prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  554. propBlockHash := propBlock.Hash()
  555. propBlockParts := propBlock.MakePartSet(partSize)
  556. incrementRound(vs2, vs3, vs4)
  557. //XXX: this isnt guaranteed to get there before the timeoutPropose ...
  558. cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
  559. <-newRoundCh
  560. t.Log("### ONTO ROUND 1")
  561. /*Round2
  562. // we timeout and prevote our lock
  563. // a polka happened but we didn't see it!
  564. */
  565. // now we're on a new round and not the proposer,
  566. // but we should receive the proposal
  567. select {
  568. case re = <-proposalCh:
  569. case <-timeoutProposeCh:
  570. re = <-proposalCh
  571. }
  572. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  573. if rs.LockedBlock != nil {
  574. panic("we should not be locked!")
  575. }
  576. t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash))
  577. // go to prevote, prevote for proposal block
  578. <-voteCh
  579. validatePrevote(t, cs1, 1, vss[0], propBlockHash)
  580. // now we see the others prevote for it, so we should lock on it
  581. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
  582. <-voteCh // precommit
  583. // we should have precommitted
  584. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
  585. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
  586. <-timeoutWaitCh
  587. incrementRound(vs2, vs3, vs4)
  588. <-newRoundCh
  589. t.Log("### ONTO ROUND 2")
  590. /*Round3
  591. we see the polka from round 1 but we shouldn't unlock!
  592. */
  593. // timeout of propose
  594. <-timeoutProposeCh
  595. // finish prevote
  596. <-voteCh
  597. // we should prevote what we're locked on
  598. validatePrevote(t, cs1, 2, vss[0], propBlockHash)
  599. newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
  600. // add prevotes from the earlier round
  601. addVotes(cs1, prevotes...)
  602. t.Log("Done adding prevotes!")
  603. ensureNoNewStep(newStepCh)
  604. }
  605. // 4 vals.
  606. // polka P0 at R0, P1 at R1, and P2 at R2,
  607. // we lock on P0 at R0, don't see P1, and unlock using P2 at R2
  608. // then we should make sure we don't lock using P1
  609. // What we want:
  610. // dont see P0, lock on P1 at R1, dont unlock using P0 at R2
  611. func TestLockPOLSafety2(t *testing.T) {
  612. cs1, vss := randConsensusState(4)
  613. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  614. partSize := config.Consensus.BlockPartSize
  615. proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
  616. timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
  617. timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
  618. newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
  619. unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
  620. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  621. // the block for R0: gets polkad but we miss it
  622. // (even though we signed it, shhh)
  623. _, propBlock0 := decideProposal(cs1, vss[0], cs1.Height, cs1.Round)
  624. propBlockHash0 := propBlock0.Hash()
  625. propBlockParts0 := propBlock0.MakePartSet(partSize)
  626. // the others sign a polka but we don't see it
  627. prevotes := signVotes(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
  628. // the block for round 1
  629. prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
  630. propBlockHash1 := propBlock1.Hash()
  631. propBlockParts1 := propBlock1.MakePartSet(partSize)
  632. propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()}
  633. incrementRound(vs2, vs3, vs4)
  634. cs1.updateRoundStep(0, RoundStepPrecommitWait)
  635. t.Log("### ONTO Round 1")
  636. // jump in at round 1
  637. height := cs1.Height
  638. startTestRound(cs1, height, 1)
  639. <-newRoundCh
  640. cs1.SetProposalAndBlock(prop1, propBlock1, propBlockParts1, "some peer")
  641. <-proposalCh
  642. <-voteCh // prevote
  643. signAddVotes(cs1, types.VoteTypePrevote, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
  644. <-voteCh // precommit
  645. // the proposed block should now be locked and our precommit added
  646. validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
  647. // add precommits from the rest
  648. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
  649. signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash1, propBlockParts1.Header(), vs3)
  650. incrementRound(vs2, vs3, vs4)
  651. // timeout of precommit wait to new round
  652. <-timeoutWaitCh
  653. // in round 2 we see the polkad block from round 0
  654. newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0, propBlockID1)
  655. if err := vs3.SignProposal(config.ChainID, newProp); err != nil {
  656. t.Fatal(err)
  657. }
  658. cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer")
  659. // Add the pol votes
  660. addVotes(cs1, prevotes...)
  661. <-newRoundCh
  662. t.Log("### ONTO Round 2")
  663. /*Round2
  664. // now we see the polka from round 1, but we shouldnt unlock
  665. */
  666. select {
  667. case <-timeoutProposeCh:
  668. <-proposalCh
  669. case <-proposalCh:
  670. }
  671. select {
  672. case <-unlockCh:
  673. panic("validator unlocked using an old polka")
  674. case <-voteCh:
  675. // prevote our locked block
  676. }
  677. validatePrevote(t, cs1, 2, vss[0], propBlockHash1)
  678. }
  679. //------------------------------------------------------------------------------------------
  680. // SlashingSuite
  681. // TODO: Slashing
  682. /*
  683. func TestSlashingPrevotes(t *testing.T) {
  684. cs1, vss := randConsensusState(2)
  685. vs2 := vss[1]
  686. proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
  687. timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
  688. newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
  689. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  690. // start round and wait for propose and prevote
  691. startTestRound(cs1, cs1.Height, 0)
  692. <-newRoundCh
  693. re := <-proposalCh
  694. <-voteCh // prevote
  695. rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
  696. // we should now be stuck in limbo forever, waiting for more prevotes
  697. // add one for a different block should cause us to go into prevote wait
  698. hash := rs.ProposalBlock.Hash()
  699. hash[0] = byte(hash[0]+1) % 255
  700. signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlockParts.Header(), vs2)
  701. <-timeoutWaitCh
  702. // NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  703. // away and ignore more prevotes (and thus fail to slash!)
  704. // add the conflicting vote
  705. signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  706. // XXX: Check for existence of Dupeout info
  707. }
  708. func TestSlashingPrecommits(t *testing.T) {
  709. cs1, vss := randConsensusState(2)
  710. vs2 := vss[1]
  711. proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
  712. timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
  713. newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
  714. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  715. // start round and wait for propose and prevote
  716. startTestRound(cs1, cs1.Height, 0)
  717. <-newRoundCh
  718. re := <-proposalCh
  719. <-voteCh // prevote
  720. // add prevote from vs2
  721. signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  722. <-voteCh // precommit
  723. // we should now be stuck in limbo forever, waiting for more prevotes
  724. // add one for a different block should cause us to go into prevote wait
  725. hash := rs.ProposalBlock.Hash()
  726. hash[0] = byte(hash[0]+1) % 255
  727. signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlockParts.Header(), vs2)
  728. // NOTE: we have to send the vote for different block first so we don't just go into precommit round right
  729. // away and ignore more prevotes (and thus fail to slash!)
  730. // add precommit from vs2
  731. signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
  732. // XXX: Check for existence of Dupeout info
  733. }
  734. */
  735. //------------------------------------------------------------------------------------------
  736. // CatchupSuite
  737. //------------------------------------------------------------------------------------------
  738. // HaltSuite
  739. // 4 vals.
  740. // we receive a final precommit after going into next round, but others might have gone to commit already!
  741. func TestHalt1(t *testing.T) {
  742. cs1, vss := randConsensusState(4)
  743. vs2, vs3, vs4 := vss[1], vss[2], vss[3]
  744. partSize := config.Consensus.BlockPartSize
  745. proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
  746. timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
  747. newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
  748. newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1)
  749. voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
  750. // start round and wait for propose and prevote
  751. startTestRound(cs1, cs1.Height, 0)
  752. <-newRoundCh
  753. re := <-proposalCh
  754. rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  755. propBlock := rs.ProposalBlock
  756. propBlockParts := propBlock.MakePartSet(partSize)
  757. <-voteCh // prevote
  758. signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlockParts.Header(), vs3, vs4)
  759. <-voteCh // precommit
  760. // the proposed block should now be locked and our precommit added
  761. validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
  762. // add precommits from the rest
  763. signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
  764. signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header(), vs3)
  765. // we receive this later, but vs3 might receive it earlier and with ours will go to commit!
  766. precommit4 := signVote(vs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
  767. incrementRound(vs2, vs3, vs4)
  768. // timeout to new round
  769. <-timeoutWaitCh
  770. re = <-newRoundCh
  771. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  772. t.Log("### ONTO ROUND 1")
  773. /*Round2
  774. // we timeout and prevote our lock
  775. // a polka happened but we didn't see it!
  776. */
  777. // go to prevote, prevote for locked block
  778. <-voteCh // prevote
  779. validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
  780. // now we receive the precommit from the previous round
  781. addVotes(cs1, precommit4)
  782. // receiving that precommit should take us straight to commit
  783. <-newBlockCh
  784. re = <-newRoundCh
  785. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
  786. if rs.Height != 2 {
  787. panic("expected height to increment")
  788. }
  789. }