github.com/vipernet-xyz/tm@v0.34.24/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/vipernet-xyz/tm/abci/example/kvstore" 15 cfg "github.com/vipernet-xyz/tm/config" 16 "github.com/vipernet-xyz/tm/libs/log" 17 tmrand "github.com/vipernet-xyz/tm/libs/rand" 18 "github.com/vipernet-xyz/tm/privval" 19 "github.com/vipernet-xyz/tm/proxy" 20 sm "github.com/vipernet-xyz/tm/state" 21 "github.com/vipernet-xyz/tm/store" 22 "github.com/vipernet-xyz/tm/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, sm.StoreOptions{ 51 DiscardABCIResponses: false, 52 }) 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(), blockExec, blockStore, mempool, evpool) 89 consensusState.SetLogger(logger) 90 consensusState.SetEventBus(eventBus) 91 if privValidator != nil { 92 consensusState.SetPrivValidator(privValidator) 93 } 94 // END OF COPY PASTE 95 96 // set consensus wal to buffered WAL, which will write all incoming msgs to buffer 97 numBlocksWritten := make(chan struct{}) 98 wal := newByteBufferWAL(logger, NewWALEncoder(wr), int64(numBlocks), numBlocksWritten) 99 // see wal.go#103 100 if err := wal.Write(EndHeightMessage{0}); err != nil { 101 t.Error(err) 102 } 103 104 consensusState.wal = wal 105 106 if err := consensusState.Start(); err != nil { 107 return fmt.Errorf("failed to start consensus state: %w", err) 108 } 109 110 select { 111 case <-numBlocksWritten: 112 if err := consensusState.Stop(); err != nil { 113 t.Error(err) 114 } 115 return nil 116 case <-time.After(1 * time.Minute): 117 if err := consensusState.Stop(); err != nil { 118 t.Error(err) 119 } 120 return fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks) 121 } 122 } 123 124 // WALWithNBlocks returns a WAL content with numBlocks. 125 func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) { 126 var b bytes.Buffer 127 wr := bufio.NewWriter(&b) 128 129 if err := WALGenerateNBlocks(t, wr, numBlocks); err != nil { 130 return []byte{}, err 131 } 132 133 wr.Flush() 134 return b.Bytes(), nil 135 } 136 137 func randPort() int { 138 // returns between base and base + spread 139 base, spread := 20000, 20000 140 return base + tmrand.Intn(spread) 141 } 142 143 func makeAddrs() (string, string, string) { 144 start := randPort() 145 return fmt.Sprintf("tcp://127.0.0.1:%d", start), 146 fmt.Sprintf("tcp://127.0.0.1:%d", start+1), 147 fmt.Sprintf("tcp://127.0.0.1:%d", start+2) 148 } 149 150 // getConfig returns a config for test cases 151 func getConfig(t *testing.T) *cfg.Config { 152 c := cfg.ResetTestRoot(t.Name()) 153 154 // and we use random ports to run in parallel 155 tm, rpc, grpc := makeAddrs() 156 c.P2P.ListenAddress = tm 157 c.RPC.ListenAddress = rpc 158 c.RPC.GRPCListenAddress = grpc 159 return c 160 } 161 162 // byteBufferWAL is a WAL which writes all msgs to a byte buffer. Writing stops 163 // when the heightToStop is reached. Client will be notified via 164 // signalWhenStopsTo channel. 165 type byteBufferWAL struct { 166 enc *WALEncoder 167 stopped bool 168 heightToStop int64 169 signalWhenStopsTo chan<- struct{} 170 171 logger log.Logger 172 } 173 174 // needed for determinism 175 var fixedTime, _ = time.Parse(time.RFC3339, "2017-01-02T15:04:05Z") 176 177 func newByteBufferWAL(logger log.Logger, enc *WALEncoder, nBlocks int64, signalStop chan<- struct{}) *byteBufferWAL { 178 return &byteBufferWAL{ 179 enc: enc, 180 heightToStop: nBlocks, 181 signalWhenStopsTo: signalStop, 182 logger: logger, 183 } 184 } 185 186 // Save writes message to the internal buffer except when heightToStop is 187 // reached, in which case it will signal the caller via signalWhenStopsTo and 188 // skip writing. 189 func (w *byteBufferWAL) Write(m WALMessage) error { 190 if w.stopped { 191 w.logger.Debug("WAL already stopped. Not writing message", "msg", m) 192 return nil 193 } 194 195 if endMsg, ok := m.(EndHeightMessage); ok { 196 w.logger.Debug("WAL write end height message", "height", endMsg.Height, "stopHeight", w.heightToStop) 197 if endMsg.Height == w.heightToStop { 198 w.logger.Debug("Stopping WAL at height", "height", endMsg.Height) 199 w.signalWhenStopsTo <- struct{}{} 200 w.stopped = true 201 return nil 202 } 203 } 204 205 w.logger.Debug("WAL Write Message", "msg", m) 206 err := w.enc.Encode(&TimedWALMessage{fixedTime, m}) 207 if err != nil { 208 panic(fmt.Sprintf("failed to encode the msg %v", m)) 209 } 210 211 return nil 212 } 213 214 func (w *byteBufferWAL) WriteSync(m WALMessage) error { 215 return w.Write(m) 216 } 217 218 func (w *byteBufferWAL) FlushAndSync() error { return nil } 219 220 func (w *byteBufferWAL) SearchForEndHeight( 221 height int64, 222 options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { 223 return nil, false, nil 224 } 225 226 func (w *byteBufferWAL) Start() error { return nil } 227 func (w *byteBufferWAL) Stop() error { return nil } 228 func (w *byteBufferWAL) Wait() {}