github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v2/reactor_test.go (about) 1 package v2 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/gogo/protobuf/proto" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 dbm "github.com/tendermint/tm-db" 15 16 abci "github.com/number571/tendermint/abci/types" 17 cfg "github.com/number571/tendermint/config" 18 "github.com/number571/tendermint/internal/blockchain/v2/internal/behavior" 19 cons "github.com/number571/tendermint/internal/consensus" 20 "github.com/number571/tendermint/internal/mempool/mock" 21 "github.com/number571/tendermint/internal/p2p" 22 "github.com/number571/tendermint/internal/p2p/conn" 23 "github.com/number571/tendermint/internal/test/factory" 24 "github.com/number571/tendermint/libs/log" 25 "github.com/number571/tendermint/libs/service" 26 bcproto "github.com/number571/tendermint/proto/tendermint/blockchain" 27 "github.com/number571/tendermint/proxy" 28 sm "github.com/number571/tendermint/state" 29 sf "github.com/number571/tendermint/state/test/factory" 30 tmstore "github.com/number571/tendermint/store" 31 "github.com/number571/tendermint/types" 32 ) 33 34 type mockPeer struct { 35 service.Service 36 id types.NodeID 37 } 38 39 func (mp mockPeer) FlushStop() {} 40 func (mp mockPeer) ID() types.NodeID { return mp.id } 41 func (mp mockPeer) RemoteIP() net.IP { return net.IP{} } 42 func (mp mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.RemoteIP(), Port: 8800} } 43 44 func (mp mockPeer) IsOutbound() bool { return true } 45 func (mp mockPeer) IsPersistent() bool { return true } 46 func (mp mockPeer) CloseConn() error { return nil } 47 48 func (mp mockPeer) NodeInfo() types.NodeInfo { 49 return types.NodeInfo{ 50 NodeID: "", 51 ListenAddr: "", 52 } 53 } 54 func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } 55 func (mp mockPeer) SocketAddr() *p2p.NetAddress { return &p2p.NetAddress{} } 56 57 func (mp mockPeer) Send(byte, []byte) bool { return true } 58 func (mp mockPeer) TrySend(byte, []byte) bool { return true } 59 60 func (mp mockPeer) Set(string, interface{}) {} 61 func (mp mockPeer) Get(string) interface{} { return struct{}{} } 62 63 //nolint:unused 64 type mockBlockStore struct { 65 blocks map[int64]*types.Block 66 } 67 68 //nolint:unused 69 func (ml *mockBlockStore) Height() int64 { 70 return int64(len(ml.blocks)) 71 } 72 73 //nolint:unused 74 func (ml *mockBlockStore) LoadBlock(height int64) *types.Block { 75 return ml.blocks[height] 76 } 77 78 //nolint:unused 79 func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) { 80 ml.blocks[block.Height] = block 81 } 82 83 type mockBlockApplier struct { 84 } 85 86 // XXX: Add whitelist/blacklist? 87 func (mba *mockBlockApplier) ApplyBlock( 88 state sm.State, blockID types.BlockID, block *types.Block, 89 ) (sm.State, error) { 90 state.LastBlockHeight++ 91 return state, nil 92 } 93 94 type mockSwitchIo struct { 95 mtx sync.Mutex 96 switchedToConsensus bool 97 numStatusResponse int 98 numBlockResponse int 99 numNoBlockResponse int 100 numStatusRequest int 101 } 102 103 var _ iIO = (*mockSwitchIo)(nil) 104 105 func (sio *mockSwitchIo) sendBlockRequest(_ p2p.Peer, _ int64) error { 106 return nil 107 } 108 109 func (sio *mockSwitchIo) sendStatusResponse(_, _ int64, _ p2p.Peer) error { 110 sio.mtx.Lock() 111 defer sio.mtx.Unlock() 112 sio.numStatusResponse++ 113 return nil 114 } 115 116 func (sio *mockSwitchIo) sendBlockToPeer(_ *types.Block, _ p2p.Peer) error { 117 sio.mtx.Lock() 118 defer sio.mtx.Unlock() 119 sio.numBlockResponse++ 120 return nil 121 } 122 123 func (sio *mockSwitchIo) sendBlockNotFound(_ int64, _ p2p.Peer) error { 124 sio.mtx.Lock() 125 defer sio.mtx.Unlock() 126 sio.numNoBlockResponse++ 127 return nil 128 } 129 130 func (sio *mockSwitchIo) trySwitchToConsensus(_ sm.State, _ bool) bool { 131 sio.mtx.Lock() 132 defer sio.mtx.Unlock() 133 sio.switchedToConsensus = true 134 return true 135 } 136 137 func (sio *mockSwitchIo) broadcastStatusRequest() error { 138 return nil 139 } 140 141 func (sio *mockSwitchIo) sendStatusRequest(_ p2p.Peer) error { 142 sio.mtx.Lock() 143 defer sio.mtx.Unlock() 144 sio.numStatusRequest++ 145 return nil 146 } 147 148 type testReactorParams struct { 149 logger log.Logger 150 genDoc *types.GenesisDoc 151 privVals []types.PrivValidator 152 startHeight int64 153 mockA bool 154 } 155 156 func newTestReactor(t *testing.T, p testReactorParams) *BlockchainReactor { 157 store, state, _ := newReactorStore(t, p.genDoc, p.privVals, p.startHeight) 158 reporter := behavior.NewMockReporter() 159 160 var appl blockApplier 161 162 if p.mockA { 163 appl = &mockBlockApplier{} 164 } else { 165 app := &testApp{} 166 cc := proxy.NewLocalClientCreator(app) 167 proxyApp := proxy.NewAppConns(cc) 168 err := proxyApp.Start() 169 require.NoError(t, err) 170 db := dbm.NewMemDB() 171 stateStore := sm.NewStore(db) 172 blockStore := tmstore.NewBlockStore(dbm.NewMemDB()) 173 appl = sm.NewBlockExecutor( 174 stateStore, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.EmptyEvidencePool{}, blockStore) 175 err = stateStore.Save(state) 176 require.NoError(t, err) 177 } 178 179 r := newReactor(state, store, reporter, appl, true, cons.NopMetrics()) 180 logger := log.TestingLogger() 181 r.SetLogger(logger.With("module", "blockchain")) 182 183 return r 184 } 185 186 // This test is left here and not deleted to retain the termination cases for 187 // future improvement in [#4482](https://github.com/number571/tendermint/issues/4482). 188 // func TestReactorTerminationScenarios(t *testing.T) { 189 190 // config := cfg.ResetTestRoot("blockchain_reactor_v2_test") 191 // defer os.RemoveAll(config.RootDir) 192 // genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30) 193 // refStore, _, _ := newReactorStore(genDoc, privVals, 20) 194 195 // params := testReactorParams{ 196 // logger: log.TestingLogger(), 197 // genDoc: genDoc, 198 // privVals: privVals, 199 // startHeight: 10, 200 // bufferSize: 100, 201 // mockA: true, 202 // } 203 204 // type testEvent struct { 205 // evType string 206 // peer string 207 // height int64 208 // } 209 210 // tests := []struct { 211 // name string 212 // params testReactorParams 213 // msgs []testEvent 214 // }{ 215 // { 216 // name: "simple termination on max peer height - one peer", 217 // params: params, 218 // msgs: []testEvent{ 219 // {evType: "AddPeer", peer: "P1"}, 220 // {evType: "ReceiveS", peer: "P1", height: 13}, 221 // {evType: "BlockReq"}, 222 // {evType: "ReceiveB", peer: "P1", height: 11}, 223 // {evType: "BlockReq"}, 224 // {evType: "BlockReq"}, 225 // {evType: "ReceiveB", peer: "P1", height: 12}, 226 // {evType: "Process"}, 227 // {evType: "ReceiveB", peer: "P1", height: 13}, 228 // {evType: "Process"}, 229 // }, 230 // }, 231 // { 232 // name: "simple termination on max peer height - two peers", 233 // params: params, 234 // msgs: []testEvent{ 235 // {evType: "AddPeer", peer: "P1"}, 236 // {evType: "AddPeer", peer: "P2"}, 237 // {evType: "ReceiveS", peer: "P1", height: 13}, 238 // {evType: "ReceiveS", peer: "P2", height: 15}, 239 // {evType: "BlockReq"}, 240 // {evType: "BlockReq"}, 241 // {evType: "ReceiveB", peer: "P1", height: 11}, 242 // {evType: "ReceiveB", peer: "P2", height: 12}, 243 // {evType: "Process"}, 244 // {evType: "BlockReq"}, 245 // {evType: "BlockReq"}, 246 // {evType: "ReceiveB", peer: "P1", height: 13}, 247 // {evType: "Process"}, 248 // {evType: "ReceiveB", peer: "P2", height: 14}, 249 // {evType: "Process"}, 250 // {evType: "BlockReq"}, 251 // {evType: "ReceiveB", peer: "P2", height: 15}, 252 // {evType: "Process"}, 253 // }, 254 // }, 255 // { 256 // name: "termination on max peer height - two peers, noBlock error", 257 // params: params, 258 // msgs: []testEvent{ 259 // {evType: "AddPeer", peer: "P1"}, 260 // {evType: "AddPeer", peer: "P2"}, 261 // {evType: "ReceiveS", peer: "P1", height: 13}, 262 // {evType: "ReceiveS", peer: "P2", height: 15}, 263 // {evType: "BlockReq"}, 264 // {evType: "BlockReq"}, 265 // {evType: "ReceiveNB", peer: "P1", height: 11}, 266 // {evType: "BlockReq"}, 267 // {evType: "ReceiveB", peer: "P2", height: 12}, 268 // {evType: "ReceiveB", peer: "P2", height: 11}, 269 // {evType: "Process"}, 270 // {evType: "BlockReq"}, 271 // {evType: "BlockReq"}, 272 // {evType: "ReceiveB", peer: "P2", height: 13}, 273 // {evType: "Process"}, 274 // {evType: "ReceiveB", peer: "P2", height: 14}, 275 // {evType: "Process"}, 276 // {evType: "BlockReq"}, 277 // {evType: "ReceiveB", peer: "P2", height: 15}, 278 // {evType: "Process"}, 279 // }, 280 // }, 281 // { 282 // name: "termination on max peer height - two peers, remove one peer", 283 // params: params, 284 // msgs: []testEvent{ 285 // {evType: "AddPeer", peer: "P1"}, 286 // {evType: "AddPeer", peer: "P2"}, 287 // {evType: "ReceiveS", peer: "P1", height: 13}, 288 // {evType: "ReceiveS", peer: "P2", height: 15}, 289 // {evType: "BlockReq"}, 290 // {evType: "BlockReq"}, 291 // {evType: "RemovePeer", peer: "P1"}, 292 // {evType: "BlockReq"}, 293 // {evType: "ReceiveB", peer: "P2", height: 12}, 294 // {evType: "ReceiveB", peer: "P2", height: 11}, 295 // {evType: "Process"}, 296 // {evType: "BlockReq"}, 297 // {evType: "BlockReq"}, 298 // {evType: "ReceiveB", peer: "P2", height: 13}, 299 // {evType: "Process"}, 300 // {evType: "ReceiveB", peer: "P2", height: 14}, 301 // {evType: "Process"}, 302 // {evType: "BlockReq"}, 303 // {evType: "ReceiveB", peer: "P2", height: 15}, 304 // {evType: "Process"}, 305 // }, 306 // }, 307 // } 308 309 // for _, tt := range tests { 310 // tt := tt 311 // t.Run(tt.name, func(t *testing.T) { 312 // reactor := newTestReactor(params) 313 // reactor.Start() 314 // reactor.reporter = behavior.NewMockReporter() 315 // mockSwitch := &mockSwitchIo{switchedToConsensus: false} 316 // reactor.io = mockSwitch 317 // // time for go routines to start 318 // time.Sleep(time.Millisecond) 319 320 // for _, step := range tt.msgs { 321 // switch step.evType { 322 // case "AddPeer": 323 // reactor.scheduler.send(bcAddNewPeer{peerID: p2p.ID(step.peer)}) 324 // case "RemovePeer": 325 // reactor.scheduler.send(bcRemovePeer{peerID: p2p.ID(step.peer)}) 326 // case "ReceiveS": 327 // reactor.scheduler.send(bcStatusResponse{ 328 // peerID: p2p.ID(step.peer), 329 // height: step.height, 330 // time: time.Now(), 331 // }) 332 // case "ReceiveB": 333 // reactor.scheduler.send(bcBlockResponse{ 334 // peerID: p2p.ID(step.peer), 335 // block: refStore.LoadBlock(step.height), 336 // size: 10, 337 // time: time.Now(), 338 // }) 339 // case "ReceiveNB": 340 // reactor.scheduler.send(bcNoBlockResponse{ 341 // peerID: p2p.ID(step.peer), 342 // height: step.height, 343 // time: time.Now(), 344 // }) 345 // case "BlockReq": 346 // reactor.scheduler.send(rTrySchedule{time: time.Now()}) 347 // case "Process": 348 // reactor.processor.send(rProcessBlock{}) 349 // } 350 // // give time for messages to propagate between routines 351 // time.Sleep(time.Millisecond) 352 // } 353 354 // // time for processor to finish and reactor to switch to consensus 355 // time.Sleep(20 * time.Millisecond) 356 // assert.True(t, mockSwitch.hasSwitchedToConsensus()) 357 // reactor.Stop() 358 // }) 359 // } 360 // } 361 362 func TestReactorHelperMode(t *testing.T) { 363 var ( 364 channelID = byte(0x40) 365 ) 366 367 config := cfg.ResetTestRoot("blockchain_reactor_v2_test") 368 defer os.RemoveAll(config.RootDir) 369 genDoc, privVals := factory.RandGenesisDoc(config, 1, false, 30) 370 371 params := testReactorParams{ 372 logger: log.TestingLogger(), 373 genDoc: genDoc, 374 privVals: privVals, 375 startHeight: 20, 376 mockA: true, 377 } 378 379 type testEvent struct { 380 peer string 381 event interface{} 382 } 383 384 tests := []struct { 385 name string 386 params testReactorParams 387 msgs []testEvent 388 }{ 389 { 390 name: "status request", 391 params: params, 392 msgs: []testEvent{ 393 {"P1", bcproto.StatusRequest{}}, 394 {"P1", bcproto.BlockRequest{Height: 13}}, 395 {"P1", bcproto.BlockRequest{Height: 20}}, 396 {"P1", bcproto.BlockRequest{Height: 22}}, 397 }, 398 }, 399 } 400 401 for _, tt := range tests { 402 tt := tt 403 t.Run(tt.name, func(t *testing.T) { 404 reactor := newTestReactor(t, params) 405 mockSwitch := &mockSwitchIo{switchedToConsensus: false} 406 reactor.io = mockSwitch 407 err := reactor.Start() 408 require.NoError(t, err) 409 410 for i := 0; i < len(tt.msgs); i++ { 411 step := tt.msgs[i] 412 switch ev := step.event.(type) { 413 case bcproto.StatusRequest: 414 old := mockSwitch.numStatusResponse 415 416 msgProto := new(bcproto.Message) 417 require.NoError(t, msgProto.Wrap(&ev)) 418 419 msgBz, err := proto.Marshal(msgProto) 420 require.NoError(t, err) 421 422 reactor.Receive(channelID, mockPeer{id: types.NodeID(step.peer)}, msgBz) 423 assert.Equal(t, old+1, mockSwitch.numStatusResponse) 424 case bcproto.BlockRequest: 425 if ev.Height > params.startHeight { 426 old := mockSwitch.numNoBlockResponse 427 428 msgProto := new(bcproto.Message) 429 require.NoError(t, msgProto.Wrap(&ev)) 430 431 msgBz, err := proto.Marshal(msgProto) 432 require.NoError(t, err) 433 434 reactor.Receive(channelID, mockPeer{id: types.NodeID(step.peer)}, msgBz) 435 assert.Equal(t, old+1, mockSwitch.numNoBlockResponse) 436 } else { 437 old := mockSwitch.numBlockResponse 438 439 msgProto := new(bcproto.Message) 440 require.NoError(t, msgProto.Wrap(&ev)) 441 442 msgBz, err := proto.Marshal(msgProto) 443 require.NoError(t, err) 444 445 reactor.Receive(channelID, mockPeer{id: types.NodeID(step.peer)}, msgBz) 446 assert.Equal(t, old+1, mockSwitch.numBlockResponse) 447 } 448 } 449 } 450 err = reactor.Stop() 451 require.NoError(t, err) 452 }) 453 } 454 } 455 456 func TestReactorSetSwitchNil(t *testing.T) { 457 config := cfg.ResetTestRoot("blockchain_reactor_v2_test") 458 defer os.RemoveAll(config.RootDir) 459 genDoc, privVals := factory.RandGenesisDoc(config, 1, false, 30) 460 461 reactor := newTestReactor(t, testReactorParams{ 462 logger: log.TestingLogger(), 463 genDoc: genDoc, 464 privVals: privVals, 465 }) 466 reactor.SetSwitch(nil) 467 468 assert.Nil(t, reactor.Switch) 469 assert.Nil(t, reactor.io) 470 } 471 472 type testApp struct { 473 abci.BaseApplication 474 } 475 476 func newReactorStore( 477 t *testing.T, 478 genDoc *types.GenesisDoc, 479 privVals []types.PrivValidator, 480 maxBlockHeight int64) (*tmstore.BlockStore, sm.State, *sm.BlockExecutor) { 481 t.Helper() 482 483 require.Len(t, privVals, 1) 484 app := &testApp{} 485 cc := proxy.NewLocalClientCreator(app) 486 proxyApp := proxy.NewAppConns(cc) 487 err := proxyApp.Start() 488 if err != nil { 489 panic(fmt.Errorf("error start app: %w", err)) 490 } 491 492 stateDB := dbm.NewMemDB() 493 blockStore := tmstore.NewBlockStore(dbm.NewMemDB()) 494 stateStore := sm.NewStore(stateDB) 495 state, err := sm.MakeGenesisState(genDoc) 496 require.NoError(t, err) 497 498 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 499 mock.Mempool{}, sm.EmptyEvidencePool{}, blockStore) 500 err = stateStore.Save(state) 501 require.NoError(t, err) 502 503 // add blocks in 504 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 505 lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) 506 if blockHeight > 1 { 507 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 508 lastBlock := blockStore.LoadBlock(blockHeight - 1) 509 vote, err := factory.MakeVote( 510 privVals[0], 511 lastBlock.Header.ChainID, 0, 512 lastBlock.Header.Height, 0, 2, 513 lastBlockMeta.BlockID, 514 time.Now(), 515 ) 516 require.NoError(t, err) 517 lastCommit = types.NewCommit(vote.Height, vote.Round, 518 lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 519 } 520 521 thisBlock := sf.MakeBlock(state, blockHeight, lastCommit) 522 523 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 524 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 525 526 state, err = blockExec.ApplyBlock(state, blockID, thisBlock) 527 require.NoError(t, err) 528 529 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 530 } 531 return blockStore, state, blockExec 532 }