github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/blockchain/v0/reactor_test.go (about) 1 package v0 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 7 "testing" 8 "time" 9 10 "github.com/gogo/protobuf/proto" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 dbm "github.com/tendermint/tm-db" 15 16 abci "github.com/pure-x-eth/consensus_tm/abci/types" 17 cfg "github.com/pure-x-eth/consensus_tm/config" 18 "github.com/pure-x-eth/consensus_tm/libs/log" 19 "github.com/pure-x-eth/consensus_tm/mempool/mock" 20 "github.com/pure-x-eth/consensus_tm/p2p" 21 bcproto "github.com/pure-x-eth/consensus_tm/proto/tendermint/blockchain" 22 "github.com/pure-x-eth/consensus_tm/proxy" 23 sm "github.com/pure-x-eth/consensus_tm/state" 24 "github.com/pure-x-eth/consensus_tm/store" 25 "github.com/pure-x-eth/consensus_tm/types" 26 tmtime "github.com/pure-x-eth/consensus_tm/types/time" 27 ) 28 29 var config *cfg.Config 30 31 func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { 32 validators := make([]types.GenesisValidator, numValidators) 33 privValidators := make([]types.PrivValidator, numValidators) 34 for i := 0; i < numValidators; i++ { 35 val, privVal := types.RandValidator(randPower, minPower) 36 validators[i] = types.GenesisValidator{ 37 PubKey: val.PubKey, 38 Power: val.VotingPower, 39 } 40 privValidators[i] = privVal 41 } 42 sort.Sort(types.PrivValidatorsByAddress(privValidators)) 43 44 return &types.GenesisDoc{ 45 GenesisTime: tmtime.Now(), 46 ChainID: config.ChainID(), 47 Validators: validators, 48 }, privValidators 49 } 50 51 type BlockchainReactorPair struct { 52 reactor *BlockchainReactor 53 app proxy.AppConns 54 } 55 56 func newBlockchainReactor( 57 logger log.Logger, 58 genDoc *types.GenesisDoc, 59 privVals []types.PrivValidator, 60 maxBlockHeight int64) BlockchainReactorPair { 61 if len(privVals) != 1 { 62 panic("only support one validator") 63 } 64 65 app := &testApp{} 66 cc := proxy.NewLocalClientCreator(app) 67 proxyApp := proxy.NewAppConns(cc) 68 err := proxyApp.Start() 69 if err != nil { 70 panic(fmt.Errorf("error start app: %w", err)) 71 } 72 73 blockDB := dbm.NewMemDB() 74 stateDB := dbm.NewMemDB() 75 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 76 DiscardABCIResponses: false, 77 }) 78 blockStore := store.NewBlockStore(blockDB) 79 80 state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) 81 if err != nil { 82 panic(fmt.Errorf("error constructing state from genesis file: %w", err)) 83 } 84 85 // Make the BlockchainReactor itself. 86 // NOTE we have to create and commit the blocks first because 87 // pool.height is determined from the store. 88 fastSync := true 89 db := dbm.NewMemDB() 90 stateStore = sm.NewStore(db, sm.StoreOptions{ 91 DiscardABCIResponses: false, 92 }) 93 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 94 mock.Mempool{}, sm.EmptyEvidencePool{}) 95 if err = stateStore.Save(state); err != nil { 96 panic(err) 97 } 98 99 // let's add some blocks in 100 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 101 lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) 102 if blockHeight > 1 { 103 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 104 lastBlock := blockStore.LoadBlock(blockHeight - 1) 105 106 vote, err := types.MakeVote( 107 lastBlock.Header.Height, 108 lastBlockMeta.BlockID, 109 state.Validators, 110 privVals[0], 111 lastBlock.Header.ChainID, 112 time.Now(), 113 ) 114 if err != nil { 115 panic(err) 116 } 117 lastCommit = types.NewCommit(vote.Height, vote.Round, 118 lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 119 } 120 121 thisBlock := makeBlock(blockHeight, state, lastCommit) 122 123 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 124 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 125 126 state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) 127 if err != nil { 128 panic(fmt.Errorf("error apply block: %w", err)) 129 } 130 131 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 132 } 133 134 bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) 135 bcReactor.SetLogger(logger.With("module", "blockchain")) 136 137 return BlockchainReactorPair{bcReactor, proxyApp} 138 } 139 140 func TestNoBlockResponse(t *testing.T) { 141 config = cfg.ResetTestRoot("blockchain_reactor_test") 142 defer os.RemoveAll(config.RootDir) 143 genDoc, privVals := randGenesisDoc(1, false, 30) 144 145 maxBlockHeight := int64(65) 146 147 reactorPairs := make([]BlockchainReactorPair, 2) 148 149 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 150 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 151 152 p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { 153 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 154 return s 155 156 }, p2p.Connect2Switches) 157 158 defer func() { 159 for _, r := range reactorPairs { 160 err := r.reactor.Stop() 161 require.NoError(t, err) 162 err = r.app.Stop() 163 require.NoError(t, err) 164 } 165 }() 166 167 tests := []struct { 168 height int64 169 existent bool 170 }{ 171 {maxBlockHeight + 2, false}, 172 {10, true}, 173 {1, true}, 174 {100, false}, 175 } 176 177 for { 178 if reactorPairs[1].reactor.pool.IsCaughtUp() { 179 break 180 } 181 182 time.Sleep(10 * time.Millisecond) 183 } 184 185 assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height()) 186 187 for _, tt := range tests { 188 block := reactorPairs[1].reactor.store.LoadBlock(tt.height) 189 if tt.existent { 190 assert.True(t, block != nil) 191 } else { 192 assert.True(t, block == nil) 193 } 194 } 195 } 196 197 func TestLegacyReactorReceiveBasic(t *testing.T) { 198 config = cfg.ResetTestRoot("blockchain_reactor_test") 199 defer os.RemoveAll(config.RootDir) 200 genDoc, privVals := randGenesisDoc(1, false, 30) 201 reactor := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 10).reactor 202 peer := p2p.CreateRandomPeer(false) 203 204 reactor.InitPeer(peer) 205 reactor.AddPeer(peer) 206 m := &bcproto.StatusRequest{} 207 wm := m.Wrap() 208 msg, err := proto.Marshal(wm) 209 assert.NoError(t, err) 210 211 assert.NotPanics(t, func() { 212 reactor.Receive(BlockchainChannel, peer, msg) 213 }) 214 } 215 216 // NOTE: This is too hard to test without 217 // an easy way to add test peer to switch 218 // or without significant refactoring of the module. 219 // Alternatively we could actually dial a TCP conn but 220 // that seems extreme. 221 func TestBadBlockStopsPeer(t *testing.T) { 222 config = cfg.ResetTestRoot("blockchain_reactor_test") 223 defer os.RemoveAll(config.RootDir) 224 genDoc, privVals := randGenesisDoc(1, false, 30) 225 226 maxBlockHeight := int64(148) 227 228 // Other chain needs a different validator set 229 otherGenDoc, otherPrivVals := randGenesisDoc(1, false, 30) 230 otherChain := newBlockchainReactor(log.TestingLogger(), otherGenDoc, otherPrivVals, maxBlockHeight) 231 232 defer func() { 233 err := otherChain.reactor.Stop() 234 require.Error(t, err) 235 err = otherChain.app.Stop() 236 require.NoError(t, err) 237 }() 238 239 reactorPairs := make([]BlockchainReactorPair, 4) 240 241 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 242 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 243 reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 244 reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 245 246 switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch { 247 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 248 return s 249 250 }, p2p.Connect2Switches) 251 252 defer func() { 253 for _, r := range reactorPairs { 254 err := r.reactor.Stop() 255 require.NoError(t, err) 256 257 err = r.app.Stop() 258 require.NoError(t, err) 259 } 260 }() 261 262 for { 263 time.Sleep(1 * time.Second) 264 caughtUp := true 265 for _, r := range reactorPairs { 266 if !r.reactor.pool.IsCaughtUp() { 267 caughtUp = false 268 } 269 } 270 if caughtUp { 271 break 272 } 273 } 274 275 // at this time, reactors[0-3] is the newest 276 assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size()) 277 278 // Mark reactorPairs[3] as an invalid peer. Fiddling with .store without a mutex is a data 279 // race, but can't be easily avoided. 280 reactorPairs[3].reactor.store = otherChain.reactor.store 281 282 lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 283 reactorPairs = append(reactorPairs, lastReactorPair) 284 285 switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { 286 s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor) 287 return s 288 289 }, p2p.Connect2Switches)...) 290 291 for i := 0; i < len(reactorPairs)-1; i++ { 292 p2p.Connect2Switches(switches, i, len(reactorPairs)-1) 293 } 294 295 for { 296 if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 { 297 break 298 } 299 300 time.Sleep(1 * time.Second) 301 } 302 303 assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1) 304 } 305 306 //---------------------------------------------- 307 // utility funcs 308 309 func makeTxs(height int64) (txs []types.Tx) { 310 for i := 0; i < 10; i++ { 311 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 312 } 313 return txs 314 } 315 316 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 317 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 318 return block 319 } 320 321 type testApp struct { 322 abci.BaseApplication 323 } 324 325 var _ abci.Application = (*testApp)(nil) 326 327 func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { 328 return abci.ResponseInfo{} 329 } 330 331 func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { 332 return abci.ResponseBeginBlock{} 333 } 334 335 func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 336 return abci.ResponseEndBlock{} 337 } 338 339 func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 340 return abci.ResponseDeliverTx{Events: []abci.Event{}} 341 } 342 343 func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 344 return abci.ResponseCheckTx{} 345 } 346 347 func (app *testApp) Commit() abci.ResponseCommit { 348 return abci.ResponseCommit{} 349 } 350 351 func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { 352 return 353 }