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.

321 lines
8.3 KiB

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