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