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.

283 lines
7.2 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package consensus
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. "strings"
  10. bc "github.com/tendermint/tendermint/blockchain"
  11. cfg "github.com/tendermint/tendermint/config"
  12. "github.com/tendermint/tendermint/proxy"
  13. sm "github.com/tendermint/tendermint/state"
  14. "github.com/tendermint/tendermint/types"
  15. cmn "github.com/tendermint/tmlibs/common"
  16. dbm "github.com/tendermint/tmlibs/db"
  17. )
  18. //--------------------------------------------------------
  19. // replay messages interactively or all at once
  20. func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console bool) {
  21. consensusState := newConsensusStateForReplay(config, csConfig)
  22. if err := consensusState.ReplayFile(csConfig.WalFile(), console); err != nil {
  23. cmn.Exit(cmn.Fmt("Error during consensus replay: %v", err))
  24. }
  25. }
  26. // Replay msgs in file or start the console
  27. func (cs *ConsensusState) ReplayFile(file string, console bool) error {
  28. if cs.IsRunning() {
  29. return errors.New("cs is already running, cannot replay")
  30. }
  31. if cs.wal != nil {
  32. return errors.New("cs wal is open, cannot replay")
  33. }
  34. cs.startForReplay()
  35. // ensure all new step events are regenerated as expected
  36. newStepCh := subscribeToEvent(cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
  37. // just open the file for reading, no need to use wal
  38. fp, err := os.OpenFile(file, os.O_RDONLY, 0666)
  39. if err != nil {
  40. return err
  41. }
  42. pb := newPlayback(file, fp, cs, cs.state.Copy())
  43. defer pb.fp.Close()
  44. var nextN int // apply N msgs in a row
  45. var msg *TimedWALMessage
  46. for {
  47. if nextN == 0 && console {
  48. nextN = pb.replayConsoleLoop()
  49. }
  50. msg, err = pb.dec.Decode()
  51. if err == io.EOF {
  52. return nil
  53. } else if err != nil {
  54. return err
  55. }
  56. if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil {
  57. return err
  58. }
  59. if nextN > 0 {
  60. nextN -= 1
  61. }
  62. pb.count += 1
  63. }
  64. return nil
  65. }
  66. //------------------------------------------------
  67. // playback manager
  68. type playback struct {
  69. cs *ConsensusState
  70. fp *os.File
  71. dec *WALDecoder
  72. count int // how many lines/msgs into the file are we
  73. // replays can be reset to beginning
  74. fileName string // so we can close/reopen the file
  75. genesisState *sm.State // so the replay session knows where to restart from
  76. }
  77. func newPlayback(fileName string, fp *os.File, cs *ConsensusState, genState *sm.State) *playback {
  78. return &playback{
  79. cs: cs,
  80. fp: fp,
  81. fileName: fileName,
  82. genesisState: genState,
  83. dec: NewWALDecoder(fp),
  84. }
  85. }
  86. // go back count steps by resetting the state and running (pb.count - count) steps
  87. func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
  88. pb.cs.Stop()
  89. pb.cs.Wait()
  90. newCS := NewConsensusState(pb.cs.config, pb.genesisState.Copy(), pb.cs.proxyAppConn, pb.cs.blockStore, pb.cs.mempool)
  91. newCS.SetEventSwitch(pb.cs.evsw)
  92. newCS.startForReplay()
  93. pb.fp.Close()
  94. fp, err := os.OpenFile(pb.fileName, os.O_RDONLY, 0666)
  95. if err != nil {
  96. return err
  97. }
  98. pb.fp = fp
  99. pb.dec = NewWALDecoder(fp)
  100. count = pb.count - count
  101. fmt.Printf("Reseting from %d to %d\n", pb.count, count)
  102. pb.count = 0
  103. pb.cs = newCS
  104. var msg *TimedWALMessage
  105. for i := 0; i < count; i++ {
  106. msg, err = pb.dec.Decode()
  107. if err == io.EOF {
  108. return nil
  109. } else if err != nil {
  110. return err
  111. }
  112. if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil {
  113. return err
  114. }
  115. pb.count += 1
  116. }
  117. return nil
  118. }
  119. func (cs *ConsensusState) startForReplay() {
  120. cs.Logger.Error("Replay commands are disabled until someone updates them and writes tests")
  121. /* TODO:!
  122. // since we replay tocks we just ignore ticks
  123. go func() {
  124. for {
  125. select {
  126. case <-cs.tickChan:
  127. case <-cs.Quit:
  128. return
  129. }
  130. }
  131. }()*/
  132. }
  133. // console function for parsing input and running commands
  134. func (pb *playback) replayConsoleLoop() int {
  135. for {
  136. fmt.Printf("> ")
  137. bufReader := bufio.NewReader(os.Stdin)
  138. line, more, err := bufReader.ReadLine()
  139. if more {
  140. cmn.Exit("input is too long")
  141. } else if err != nil {
  142. cmn.Exit(err.Error())
  143. }
  144. tokens := strings.Split(string(line), " ")
  145. if len(tokens) == 0 {
  146. continue
  147. }
  148. switch tokens[0] {
  149. case "next":
  150. // "next" -> replay next message
  151. // "next N" -> replay next N messages
  152. if len(tokens) == 1 {
  153. return 0
  154. } else {
  155. i, err := strconv.Atoi(tokens[1])
  156. if err != nil {
  157. fmt.Println("next takes an integer argument")
  158. } else {
  159. return i
  160. }
  161. }
  162. case "back":
  163. // "back" -> go back one message
  164. // "back N" -> go back N messages
  165. // NOTE: "back" is not supported in the state machine design,
  166. // so we restart and replay up to
  167. // ensure all new step events are regenerated as expected
  168. newStepCh := subscribeToEvent(pb.cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
  169. if len(tokens) == 1 {
  170. pb.replayReset(1, newStepCh)
  171. } else {
  172. i, err := strconv.Atoi(tokens[1])
  173. if err != nil {
  174. fmt.Println("back takes an integer argument")
  175. } else if i > pb.count {
  176. fmt.Printf("argument to back must not be larger than the current count (%d)\n", pb.count)
  177. } else {
  178. pb.replayReset(i, newStepCh)
  179. }
  180. }
  181. case "rs":
  182. // "rs" -> print entire round state
  183. // "rs short" -> print height/round/step
  184. // "rs <field>" -> print another field of the round state
  185. rs := pb.cs.RoundState
  186. if len(tokens) == 1 {
  187. fmt.Println(rs)
  188. } else {
  189. switch tokens[1] {
  190. case "short":
  191. fmt.Printf("%v/%v/%v\n", rs.Height, rs.Round, rs.Step)
  192. case "validators":
  193. fmt.Println(rs.Validators)
  194. case "proposal":
  195. fmt.Println(rs.Proposal)
  196. case "proposal_block":
  197. fmt.Printf("%v %v\n", rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort())
  198. case "locked_round":
  199. fmt.Println(rs.LockedRound)
  200. case "locked_block":
  201. fmt.Printf("%v %v\n", rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort())
  202. case "votes":
  203. fmt.Println(rs.Votes.StringIndented(" "))
  204. default:
  205. fmt.Println("Unknown option", tokens[1])
  206. }
  207. }
  208. case "n":
  209. fmt.Println(pb.count)
  210. }
  211. }
  212. return 0
  213. }
  214. //--------------------------------------------------------------------------------
  215. // convenience for replay mode
  216. func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig) *ConsensusState {
  217. // Get BlockStore
  218. blockStoreDB := dbm.NewDB("blockstore", config.DBBackend, config.DBDir())
  219. blockStore := bc.NewBlockStore(blockStoreDB)
  220. // Get State
  221. stateDB := dbm.NewDB("state", config.DBBackend, config.DBDir())
  222. state, err := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile())
  223. if err != nil {
  224. cmn.Exit(err.Error())
  225. }
  226. // Create proxyAppConn connection (consensus, mempool, query)
  227. clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
  228. proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(state, blockStore))
  229. _, err = proxyApp.Start()
  230. if err != nil {
  231. cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
  232. }
  233. // Make event switch
  234. eventSwitch := types.NewEventSwitch()
  235. if _, err := eventSwitch.Start(); err != nil {
  236. cmn.Exit(cmn.Fmt("Failed to start event switch: %v", err))
  237. }
  238. consensusState := NewConsensusState(csConfig, state.Copy(), proxyApp.Consensus(), blockStore, types.MockMempool{})
  239. consensusState.SetEventSwitch(eventSwitch)
  240. return consensusState
  241. }