github.com/okex/exchain@v1.8.0/libs/tendermint/consensus/wal_generator.go (about) 1 package consensus 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 tmtime "github.com/okex/exchain/libs/tendermint/types/time" 8 "io" 9 "path/filepath" 10 "testing" 11 "time" 12 13 "github.com/pkg/errors" 14 15 db "github.com/okex/exchain/libs/tm-db" 16 17 "github.com/okex/exchain/libs/tendermint/abci/example/kvstore" 18 cfg "github.com/okex/exchain/libs/tendermint/config" 19 "github.com/okex/exchain/libs/tendermint/libs/log" 20 tmrand "github.com/okex/exchain/libs/tendermint/libs/rand" 21 "github.com/okex/exchain/libs/tendermint/mock" 22 "github.com/okex/exchain/libs/tendermint/privval" 23 "github.com/okex/exchain/libs/tendermint/proxy" 24 sm "github.com/okex/exchain/libs/tendermint/state" 25 "github.com/okex/exchain/libs/tendermint/store" 26 "github.com/okex/exchain/libs/tendermint/types" 27 ) 28 29 // WALGenerateNBlocks generates a consensus WAL. It does this by spinning up a 30 // stripped down version of node (proxy app, event bus, consensus state) with a 31 // persistent kvstore application and special consensus wal instance 32 // (byteBufferWAL) and waits until numBlocks are created. 33 // If the node fails to produce given numBlocks, it returns an error. 34 func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { 35 config := getConfig(t) 36 37 app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) 38 39 logger := log.TestingLogger().With("wal_generator", "wal_generator") 40 logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks) 41 42 ///////////////////////////////////////////////////////////////////////////// 43 // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS 44 // NOTE: we can't import node package because of circular dependency. 45 // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly. 46 privValidatorKeyFile := config.PrivValidatorKeyFile() 47 privValidatorStateFile := config.PrivValidatorStateFile() 48 privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) 49 genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) 50 if err != nil { 51 return errors.Wrap(err, "failed to read genesis file") 52 } 53 blockStoreDB := db.NewMemDB() 54 stateDB := blockStoreDB 55 state, err := sm.MakeGenesisState(genDoc) 56 if err != nil { 57 return errors.Wrap(err, "failed to make genesis state") 58 } 59 state.Version.Consensus.App = kvstore.ProtocolVersion 60 sm.SaveState(stateDB, state) 61 blockStore := store.NewBlockStore(blockStoreDB) 62 63 proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app)) 64 proxyApp.SetLogger(logger.With("module", "proxy")) 65 if err := proxyApp.Start(); err != nil { 66 return errors.Wrap(err, "failed to start proxy app connections") 67 } 68 defer proxyApp.Stop() 69 70 eventBus := types.NewEventBus() 71 eventBus.SetLogger(logger.With("module", "events")) 72 if err := eventBus.Start(); err != nil { 73 return errors.Wrap(err, "failed to start event bus") 74 } 75 defer eventBus.Stop() 76 mempool := mock.Mempool{} 77 evpool := sm.MockEvidencePool{} 78 blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) 79 consensusState := NewState(config.Consensus, state.Copy(), blockExec, blockStore, mempool, evpool) 80 consensusState.SetLogger(logger) 81 consensusState.SetEventBus(eventBus) 82 if privValidator != nil { 83 consensusState.SetPrivValidator(privValidator) 84 } 85 // END OF COPY PASTE 86 ///////////////////////////////////////////////////////////////////////////// 87 88 // set consensus wal to buffered WAL, which will write all incoming msgs to buffer 89 numBlocksWritten := make(chan struct{}) 90 wal := newByteBufferWAL(logger, NewWALEncoder(wr), int64(numBlocks), numBlocksWritten) 91 // see wal.go#103 92 wal.Write(EndHeightMessage{0}) 93 consensusState.wal = wal 94 95 if err := consensusState.Start(); err != nil { 96 return errors.Wrap(err, "failed to start consensus state") 97 } 98 99 select { 100 case <-numBlocksWritten: 101 consensusState.Stop() 102 return nil 103 case <-time.After(1 * time.Minute): 104 consensusState.Stop() 105 return fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks) 106 } 107 } 108 109 //WALWithNBlocks returns a WAL content with numBlocks. 110 func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) { 111 var b bytes.Buffer 112 wr := bufio.NewWriter(&b) 113 114 if err := WALGenerateNBlocks(t, wr, numBlocks); err != nil { 115 return []byte{}, err 116 } 117 118 wr.Flush() 119 return b.Bytes(), nil 120 } 121 122 func randPort() int { 123 // returns between base and base + spread 124 base, spread := 20000, 20000 125 return base + tmrand.Intn(spread) 126 } 127 128 func makeAddrs() (string, string, string) { 129 start := randPort() 130 return fmt.Sprintf("tcp://127.0.0.1:%d", start), 131 fmt.Sprintf("tcp://127.0.0.1:%d", start+1), 132 fmt.Sprintf("tcp://127.0.0.1:%d", start+2) 133 } 134 135 // getConfig returns a config for test cases 136 func getConfig(t *testing.T) *cfg.Config { 137 c := cfg.ResetTestRoot(t.Name()) 138 139 // and we use random ports to run in parallel 140 tm, rpc, grpc := makeAddrs() 141 c.P2P.ListenAddress = tm 142 c.RPC.ListenAddress = rpc 143 c.RPC.GRPCListenAddress = grpc 144 return c 145 } 146 147 // byteBufferWAL is a WAL which writes all msgs to a byte buffer. Writing stops 148 // when the heightToStop is reached. Client will be notified via 149 // signalWhenStopsTo channel. 150 type byteBufferWAL struct { 151 enc *WALEncoder 152 stopped bool 153 heightToStop int64 154 signalWhenStopsTo chan<- struct{} 155 156 logger log.Logger 157 } 158 159 // needed for determinism 160 var fixedTime, _ = time.Parse(time.RFC3339, "2017-01-02T15:04:05Z") 161 162 func newByteBufferWAL(logger log.Logger, enc *WALEncoder, nBlocks int64, signalStop chan<- struct{}) *byteBufferWAL { 163 return &byteBufferWAL{ 164 enc: enc, 165 heightToStop: nBlocks, 166 signalWhenStopsTo: signalStop, 167 logger: logger, 168 } 169 } 170 171 // Save writes message to the internal buffer except when heightToStop is 172 // reached, in which case it will signal the caller via signalWhenStopsTo and 173 // skip writing. 174 func (w *byteBufferWAL) Write(m WALMessage) error { 175 t0 := tmtime.Now() 176 if w.stopped { 177 w.logger.Debug("WAL already stopped. Not writing message", "msg", m) 178 return nil 179 } 180 181 if endMsg, ok := m.(EndHeightMessage); ok { 182 w.logger.Debug("WAL write end height message", "height", endMsg.Height, "stopHeight", w.heightToStop) 183 if endMsg.Height == w.heightToStop { 184 w.logger.Debug("Stopping WAL at height", "height", endMsg.Height) 185 w.signalWhenStopsTo <- struct{}{} 186 w.stopped = true 187 return nil 188 } 189 } 190 191 w.logger.Debug("WAL Write Message", "msg", m) 192 err := w.enc.Encode(&TimedWALMessage{fixedTime, m}) 193 if err != nil { 194 panic(fmt.Sprintf("failed to encode the msg %v", m)) 195 } 196 if t := tmtime.Now().Sub(t0); t > walAlertTime { 197 w.logger.Error("WAL Write Message", "time", t, "msg", m) 198 } 199 200 return nil 201 } 202 203 func (w *byteBufferWAL) WriteSync(m WALMessage) error { 204 return w.Write(m) 205 } 206 207 func (w *byteBufferWAL) FlushAndSync() error { return nil } 208 209 func (w *byteBufferWAL) SearchForEndHeight( 210 height int64, 211 options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { 212 return nil, false, nil 213 } 214 215 func (w *byteBufferWAL) Start() error { return nil } 216 func (w *byteBufferWAL) Stop() error { return nil } 217 func (w *byteBufferWAL) Wait() {} 218 func (w *byteBufferWAL) Reset() error { return nil }