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