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