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