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