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