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.

202 lines
7.6 KiB

8 years ago
8 years ago
8 years ago
  1. package consensus
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "strings"
  7. "testing"
  8. "time"
  9. . "github.com/tendermint/go-common"
  10. "github.com/tendermint/go-wire"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. /*
  14. The easiest way to generate this data is to copy ~/.tendermint_test/somedir/* to ~/.tendermint
  15. and to run a local node.
  16. Be sure to set the db to "leveldb" to create a cswal file in ~/.tendermint/data/cswal.
  17. If you need to change the signatures, you can use a script as follows:
  18. The privBytes comes from config/tendermint_test/...
  19. ```
  20. package main
  21. import (
  22. "encoding/hex"
  23. "fmt"
  24. "github.com/tendermint/go-crypto"
  25. )
  26. func main() {
  27. signBytes, err := hex.DecodeString("7B22636861696E5F6964223A2274656E6465726D696E745F74657374222C22766F7465223A7B22626C6F636B5F68617368223A2242453544373939433846353044354645383533364334333932464443384537423342313830373638222C22626C6F636B5F70617274735F686561646572223A506172745365747B543A31204236323237323535464632307D2C22686569676874223A312C22726F756E64223A302C2274797065223A327D7D")
  28. if err != nil {
  29. panic(err)
  30. }
  31. privBytes, err := hex.DecodeString("27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8")
  32. if err != nil {
  33. panic(err)
  34. }
  35. privKey := crypto.PrivKeyEd25519{}
  36. copy(privKey[:], privBytes)
  37. signature := privKey.Sign(signBytes)
  38. signatureEd25519 := signature.(crypto.SignatureEd25519)
  39. fmt.Printf("Signature Bytes: %X\n", signatureEd25519[:])
  40. }
  41. ```
  42. */
  43. var testLog = `{"time":"2016-04-03T11:23:54.387Z","msg":[3,{"duration":972835254,"height":1,"round":0,"step":1}]}
  44. {"time":"2016-04-03T11:23:54.388Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPropose"}]}
  45. {"time":"2016-04-03T11:23:54.388Z","msg":[2,{"msg":[17,{"Proposal":{"height":1,"round":0,"block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"pol_round":-1,"signature":"3A2ECD5023B21EC144EC16CFF1B992A4321317B83EEDD8969FDFEA6EB7BF4389F38DDA3E7BB109D63A07491C16277A197B241CF1F05F5E485C59882ECACD9E07"}}],"peer_key":""}]}
  46. {"time":"2016-04-03T11:23:54.389Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":0,"bytes":"0101010F74656E6465726D696E745F7465737401011441D59F4B718AC00000000000000114C4B01D3810579550997AC5641E759E20D99B51C10001000100","proof":{"aunts":[]}}}],"peer_key":""}]}
  47. {"time":"2016-04-03T11:23:54.390Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrevote"}]}
  48. {"time":"2016-04-03T11:23:54.390Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":1,"round":0,"type":1,"block_hash":"4291966B8A9DFBA00AEC7C700F2718E61DF4331D","block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"signature":"47D2A75A4E2F15DB1F0D1B656AC0637AF9AADDFEB6A156874F6553C73895E5D5DC948DBAEF15E61276C5342D0E638DFCB77C971CD282096EA8735A564A90F008"}}],"peer_key":""}]}
  49. {"time":"2016-04-03T11:23:54.392Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrecommit"}]}
  50. {"time":"2016-04-03T11:23:54.392Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":1,"round":0,"type":2,"block_hash":"4291966B8A9DFBA00AEC7C700F2718E61DF4331D","block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"signature":"39147DA595F08B73CF8C899967C8403B5872FD9042FFA4E239159E0B6C5D9665C9CA81D766EACA2AE658872F94C2FCD1E34BF51859CD5B274DA8512BACE4B50D"}}],"peer_key":""}]}
  51. `
  52. // map lines in the above wal to privVal step
  53. var mapPrivValStep = map[int]int8{
  54. 0: 0,
  55. 1: 0,
  56. 2: 1,
  57. 3: 1,
  58. 4: 1,
  59. 5: 2,
  60. 6: 2,
  61. 7: 3,
  62. }
  63. func writeWAL(log string) string {
  64. fmt.Println("writing", log)
  65. // write the needed wal to file
  66. f, err := ioutil.TempFile(os.TempDir(), "replay_test_")
  67. if err != nil {
  68. panic(err)
  69. }
  70. _, err = f.WriteString(log)
  71. if err != nil {
  72. panic(err)
  73. }
  74. name := f.Name()
  75. f.Close()
  76. return name
  77. }
  78. func waitForBlock(newBlockCh chan interface{}) {
  79. after := time.After(time.Second * 10)
  80. select {
  81. case <-newBlockCh:
  82. case <-after:
  83. panic("Timed out waiting for new block")
  84. }
  85. }
  86. func runReplayTest(t *testing.T, cs *ConsensusState, fileName string, newBlockCh chan interface{}) {
  87. cs.config.Set("cswal", fileName)
  88. cs.Start()
  89. // Wait to make a new block.
  90. // This is just a signal that we haven't halted; its not something contained in the WAL itself.
  91. // Assuming the consensus state is running, replay of any WAL, including the empty one,
  92. // should eventually be followed by a new block, or else something is wrong
  93. waitForBlock(newBlockCh)
  94. cs.Stop()
  95. }
  96. func setupReplayTest(nLines int, crashAfter bool) (*ConsensusState, chan interface{}, string, string) {
  97. fmt.Println("-------------------------------------")
  98. log.Notice(Fmt("Starting replay test of %d lines of WAL (crash before write)", nLines))
  99. lineStep := nLines
  100. if crashAfter {
  101. lineStep -= 1
  102. }
  103. split := strings.Split(testLog, "\n")
  104. lastMsg := split[nLines]
  105. // we write those lines up to (not including) one with the signature
  106. fileName := writeWAL(strings.Join(split[:nLines], "\n") + "\n")
  107. cs := fixedConsensusState()
  108. // set the last step according to when we crashed vs the wal
  109. cs.privValidator.LastHeight = 1 // first block
  110. cs.privValidator.LastStep = mapPrivValStep[lineStep]
  111. fmt.Println("LAST STEP", cs.privValidator.LastStep)
  112. newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
  113. return cs, newBlockCh, lastMsg, fileName
  114. }
  115. //-----------------------------------------------
  116. // Test the log at every iteration, and set the privVal last step
  117. // as if the log was written after signing, before the crash
  118. func TestReplayCrashAfterWrite(t *testing.T) {
  119. split := strings.Split(testLog, "\n")
  120. for i := 0; i < len(split)-1; i++ {
  121. cs, newBlockCh, _, f := setupReplayTest(i+1, true)
  122. runReplayTest(t, cs, f, newBlockCh)
  123. }
  124. }
  125. //-----------------------------------------------
  126. // Test the log as if we crashed after signing but before writing.
  127. // This relies on privValidator.LastSignature being set
  128. func TestReplayCrashBeforeWritePropose(t *testing.T) {
  129. cs, newBlockCh, proposalMsg, f := setupReplayTest(2, false) // propose
  130. // Set LastSig
  131. var err error
  132. var msg ConsensusLogMessage
  133. wire.ReadJSON(&msg, []byte(proposalMsg), &err)
  134. proposal := msg.Msg.(msgInfo).Msg.(*ProposalMessage)
  135. if err != nil {
  136. t.Fatalf("Error reading json data: %v", err)
  137. }
  138. cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal)
  139. cs.privValidator.LastSignature = proposal.Proposal.Signature
  140. runReplayTest(t, cs, f, newBlockCh)
  141. }
  142. func TestReplayCrashBeforeWritePrevote(t *testing.T) {
  143. cs, newBlockCh, voteMsg, f := setupReplayTest(5, false) // prevote
  144. types.AddListenerForEvent(cs.evsw, "tester", types.EventStringCompleteProposal(), func(data types.TMEventData) {
  145. // Set LastSig
  146. var err error
  147. var msg ConsensusLogMessage
  148. wire.ReadJSON(&msg, []byte(voteMsg), &err)
  149. vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
  150. if err != nil {
  151. t.Fatalf("Error reading json data: %v", err)
  152. }
  153. cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
  154. cs.privValidator.LastSignature = vote.Vote.Signature
  155. })
  156. runReplayTest(t, cs, f, newBlockCh)
  157. }
  158. func TestReplayCrashBeforeWritePrecommit(t *testing.T) {
  159. cs, newBlockCh, voteMsg, f := setupReplayTest(7, false) // precommit
  160. types.AddListenerForEvent(cs.evsw, "tester", types.EventStringPolka(), func(data types.TMEventData) {
  161. // Set LastSig
  162. var err error
  163. var msg ConsensusLogMessage
  164. wire.ReadJSON(&msg, []byte(voteMsg), &err)
  165. vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
  166. if err != nil {
  167. t.Fatalf("Error reading json data: %v", err)
  168. }
  169. cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
  170. cs.privValidator.LastSignature = vote.Vote.Signature
  171. })
  172. runReplayTest(t, cs, f, newBlockCh)
  173. }