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.

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