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