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.

1059 lines
35 KiB

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
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
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
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
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
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
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
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 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
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
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
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
8 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. cstypes "github.com/tendermint/tendermint/consensus/types"
  8. "github.com/tendermint/tendermint/types"
  9. cmn "github.com/tendermint/tmlibs/common"
  10. )
  11. func init() {
  12. config = ResetConfig("consensus_state_test")
  13. }
  14. func ensureProposeTimeout(timeoutPropose int) time.Duration {
  15. return time.Duration(timeoutPropose*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].GetAddress()) {
  67. panic(cmn.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)].GetAddress()) {
  83. panic(cmn.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(ensureProposeTimeout(cs.config.TimeoutPropose))
  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(ensureProposeTimeout(cs.config.TimeoutPropose))
  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 := cs1.state.Params.BlockPartSizeBytes
  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.ChainID, 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.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.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 := cs1.state.Params.BlockPartSizeBytes
  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.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.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. t.Log("#### 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.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.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. t.Log("#### ONTO ROUND 2")
  319. /*
  320. Round3 (vs2, _) // B, B2
  321. */
  322. incrementRound(vs2)
  323. re = <-proposalCh
  324. rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.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(cmn.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. t.Log("#### 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 := cs1.state.Params.BlockPartSizeBytes
  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. // 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.(*cstypes.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.(*cstypes.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 := cs1.state.Params.BlockPartSizeBytes
  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.(*cstypes.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.(*cstypes.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 := cs1.state.Params.BlockPartSizeBytes
  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.(*cstypes.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.(*cstypes.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 := cs1.state.Params.BlockPartSizeBytes
  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, cstypes.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.(*cstypes.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 := cs1.state.Params.BlockPartSizeBytes
  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.(*cstypes.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.(*cstypes.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.(*cstypes.RoundState)
  786. if rs.Height != 2 {
  787. panic("expected height to increment")
  788. }
  789. }