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.

231 lines
7.0 KiB

8 years ago
8 years ago
9 years ago
8 years ago
8 years ago
  1. package consensus
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path"
  7. "strings"
  8. "testing"
  9. "time"
  10. "github.com/tendermint/tendermint/config/tendermint_test"
  11. . "github.com/tendermint/go-common"
  12. "github.com/tendermint/go-wire"
  13. "github.com/tendermint/tendermint/types"
  14. )
  15. func init() {
  16. config = tendermint_test.ResetConfig("consensus_replay_test")
  17. }
  18. // TODO: these tests ensure we can always recover from any state of the wal,
  19. // assuming it comes with a correct related state for the priv_validator.json.
  20. // It would be better to verify explicitly which states we can recover from without the wal
  21. // and which ones we need the wal for - then we'd also be able to only flush the
  22. // wal writer when we need to, instead of with every message.
  23. var data_dir = path.Join(GoPath, "src/github.com/tendermint/tendermint/consensus", "test_data")
  24. // the priv validator changes step at these lines for a block with 1 val and 1 part
  25. var baseStepChanges = []int{3, 6, 8}
  26. // test recovery from each line in each testCase
  27. var testCases = []*testCase{
  28. newTestCase("empty_block", baseStepChanges), // empty block (has 1 block part)
  29. newTestCase("small_block1", baseStepChanges), // small block with txs in 1 block part
  30. newTestCase("small_block2", []int{3, 10, 12}), // small block with txs across 5 smaller block parts
  31. }
  32. type testCase struct {
  33. name string
  34. log string //full cs wal
  35. stepMap map[int]int8 // map lines of log to privval step
  36. proposeLine int
  37. prevoteLine int
  38. precommitLine int
  39. }
  40. func newTestCase(name string, stepChanges []int) *testCase {
  41. if len(stepChanges) != 3 {
  42. panic(Fmt("a full wal has 3 step changes! Got array %v", stepChanges))
  43. }
  44. return &testCase{
  45. name: name,
  46. log: readWAL(path.Join(data_dir, name+".cswal")),
  47. stepMap: newMapFromChanges(stepChanges),
  48. proposeLine: stepChanges[0],
  49. prevoteLine: stepChanges[1],
  50. precommitLine: stepChanges[2],
  51. }
  52. }
  53. func newMapFromChanges(changes []int) map[int]int8 {
  54. changes = append(changes, changes[2]+1) // so we add the last step change to the map
  55. m := make(map[int]int8)
  56. var count int
  57. for changeNum, nextChange := range changes {
  58. for ; count < nextChange; count++ {
  59. m[count] = int8(changeNum)
  60. }
  61. }
  62. return m
  63. }
  64. func readWAL(p string) string {
  65. b, err := ioutil.ReadFile(p)
  66. if err != nil {
  67. panic(err)
  68. }
  69. return string(b)
  70. }
  71. func writeWAL(walMsgs string) string {
  72. tempDir := os.TempDir()
  73. walDir := tempDir + "/wal" + RandStr(12)
  74. // Create WAL directory
  75. err := EnsureDir(walDir, 0700)
  76. if err != nil {
  77. panic(err)
  78. }
  79. // Write the needed WAL to file
  80. err = WriteFile(walDir+"/wal", []byte(walMsgs), 0600)
  81. if err != nil {
  82. panic(err)
  83. }
  84. return walDir
  85. }
  86. func waitForBlock(newBlockCh chan interface{}, thisCase *testCase, i int) {
  87. after := time.After(time.Second * 10)
  88. select {
  89. case <-newBlockCh:
  90. case <-after:
  91. panic(Fmt("Timed out waiting for new block for case '%s' line %d", thisCase.name, i))
  92. }
  93. }
  94. func runReplayTest(t *testing.T, cs *ConsensusState, walDir string, newBlockCh chan interface{},
  95. thisCase *testCase, i int) {
  96. cs.config.Set("cs_wal_dir", walDir)
  97. cs.Start()
  98. // Wait to make a new block.
  99. // This is just a signal that we haven't halted; its not something contained in the WAL itself.
  100. // Assuming the consensus state is running, replay of any WAL, including the empty one,
  101. // should eventually be followed by a new block, or else something is wrong
  102. waitForBlock(newBlockCh, thisCase, i)
  103. cs.evsw.Stop()
  104. cs.Stop()
  105. LOOP:
  106. for {
  107. select {
  108. case <-newBlockCh:
  109. default:
  110. break LOOP
  111. }
  112. }
  113. cs.Wait()
  114. }
  115. func toPV(pv PrivValidator) *types.PrivValidator {
  116. return pv.(*types.PrivValidator)
  117. }
  118. func setupReplayTest(thisCase *testCase, nLines int, crashAfter bool) (*ConsensusState, chan interface{}, string, string) {
  119. fmt.Println("-------------------------------------")
  120. log.Notice(Fmt("Starting replay test %v (of %d lines of WAL). Crash after = %v", thisCase.name, nLines, crashAfter))
  121. lineStep := nLines
  122. if crashAfter {
  123. lineStep -= 1
  124. }
  125. split := strings.Split(thisCase.log, "\n")
  126. lastMsg := split[nLines]
  127. // we write those lines up to (not including) one with the signature
  128. walDir := writeWAL(strings.Join(split[:nLines], "\n") + "\n")
  129. cs := fixedConsensusStateDummy()
  130. // set the last step according to when we crashed vs the wal
  131. toPV(cs.privValidator).LastHeight = 1 // first block
  132. toPV(cs.privValidator).LastStep = thisCase.stepMap[lineStep]
  133. log.Warn("setupReplayTest", "LastStep", toPV(cs.privValidator).LastStep)
  134. newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
  135. return cs, newBlockCh, lastMsg, walDir
  136. }
  137. func readTimedWALMessage(t *testing.T, walMsg string) TimedWALMessage {
  138. var err error
  139. var msg TimedWALMessage
  140. wire.ReadJSON(&msg, []byte(walMsg), &err)
  141. if err != nil {
  142. t.Fatalf("Error reading json data: %v", err)
  143. }
  144. return msg
  145. }
  146. //-----------------------------------------------
  147. // Test the log at every iteration, and set the privVal last step
  148. // as if the log was written after signing, before the crash
  149. func TestReplayCrashAfterWrite(t *testing.T) {
  150. for _, thisCase := range testCases {
  151. split := strings.Split(thisCase.log, "\n")
  152. for i := 0; i < len(split)-1; i++ {
  153. cs, newBlockCh, _, walDir := setupReplayTest(thisCase, i+1, true)
  154. runReplayTest(t, cs, walDir, newBlockCh, thisCase, i+1)
  155. }
  156. }
  157. }
  158. //-----------------------------------------------
  159. // Test the log as if we crashed after signing but before writing.
  160. // This relies on privValidator.LastSignature being set
  161. func TestReplayCrashBeforeWritePropose(t *testing.T) {
  162. for _, thisCase := range testCases {
  163. lineNum := thisCase.proposeLine
  164. // setup replay test where last message is a proposal
  165. cs, newBlockCh, proposalMsg, walDir := setupReplayTest(thisCase, lineNum, false)
  166. msg := readTimedWALMessage(t, proposalMsg)
  167. proposal := msg.Msg.(msgInfo).Msg.(*ProposalMessage)
  168. // Set LastSig
  169. toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal)
  170. toPV(cs.privValidator).LastSignature = proposal.Proposal.Signature
  171. runReplayTest(t, cs, walDir, newBlockCh, thisCase, lineNum)
  172. }
  173. }
  174. func TestReplayCrashBeforeWritePrevote(t *testing.T) {
  175. for _, thisCase := range testCases {
  176. testReplayCrashBeforeWriteVote(t, thisCase, thisCase.prevoteLine, types.EventStringCompleteProposal())
  177. }
  178. }
  179. func TestReplayCrashBeforeWritePrecommit(t *testing.T) {
  180. for _, thisCase := range testCases {
  181. testReplayCrashBeforeWriteVote(t, thisCase, thisCase.precommitLine, types.EventStringPolka())
  182. }
  183. }
  184. func testReplayCrashBeforeWriteVote(t *testing.T, thisCase *testCase, lineNum int, eventString string) {
  185. // setup replay test where last message is a vote
  186. cs, newBlockCh, voteMsg, walDir := setupReplayTest(thisCase, lineNum, false)
  187. types.AddListenerForEvent(cs.evsw, "tester", eventString, func(data types.TMEventData) {
  188. msg := readTimedWALMessage(t, voteMsg)
  189. vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
  190. // Set LastSig
  191. toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
  192. toPV(cs.privValidator).LastSignature = vote.Vote.Signature
  193. })
  194. runReplayTest(t, cs, walDir, newBlockCh, thisCase, lineNum)
  195. }