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.

1024 lines
34 KiB

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