github.com/Team-Kujira/tendermint@v0.34.24-indexer/blockchain/v1/reactor_test.go (about) 1 package v1 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 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 15 dbm "github.com/tendermint/tm-db" 16 17 abci "github.com/tendermint/tendermint/abci/types" 18 cfg "github.com/tendermint/tendermint/config" 19 "github.com/tendermint/tendermint/libs/log" 20 "github.com/tendermint/tendermint/mempool/mock" 21 "github.com/tendermint/tendermint/p2p" 22 bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain" 23 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 24 "github.com/tendermint/tendermint/proxy" 25 sm "github.com/tendermint/tendermint/state" 26 "github.com/tendermint/tendermint/store" 27 "github.com/tendermint/tendermint/types" 28 tmtime "github.com/tendermint/tendermint/types/time" 29 ) 30 31 var config *cfg.Config 32 33 func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { 34 validators := make([]types.GenesisValidator, numValidators) 35 privValidators := make([]types.PrivValidator, numValidators) 36 for i := 0; i < numValidators; i++ { 37 val, privVal := types.RandValidator(randPower, minPower) 38 validators[i] = types.GenesisValidator{ 39 PubKey: val.PubKey, 40 Power: val.VotingPower, 41 } 42 privValidators[i] = privVal 43 } 44 sort.Sort(types.PrivValidatorsByAddress(privValidators)) 45 46 return &types.GenesisDoc{ 47 GenesisTime: tmtime.Now(), 48 ChainID: config.ChainID(), 49 Validators: validators, 50 }, privValidators 51 } 52 53 func makeVote( 54 t *testing.T, 55 header *types.Header, 56 blockID types.BlockID, 57 valset *types.ValidatorSet, 58 privVal types.PrivValidator) *types.Vote { 59 60 pubKey, err := privVal.GetPubKey() 61 require.NoError(t, err) 62 63 valIdx, _ := valset.GetByAddress(pubKey.Address()) 64 vote := &types.Vote{ 65 ValidatorAddress: pubKey.Address(), 66 ValidatorIndex: valIdx, 67 Height: header.Height, 68 Round: 1, 69 Timestamp: tmtime.Now(), 70 Type: tmproto.PrecommitType, 71 BlockID: blockID, 72 } 73 74 vpb := vote.ToProto() 75 76 _ = privVal.SignVote(header.ChainID, vpb) 77 vote.Signature = vpb.Signature 78 79 return vote 80 } 81 82 type BlockchainReactorPair struct { 83 bcR *BlockchainReactor 84 conR *consensusReactorTest 85 } 86 87 func newBlockchainReactor( 88 t *testing.T, 89 logger log.Logger, 90 genDoc *types.GenesisDoc, 91 privVals []types.PrivValidator, 92 maxBlockHeight int64) *BlockchainReactor { 93 if len(privVals) != 1 { 94 panic("only support one validator") 95 } 96 97 app := &testApp{} 98 cc := proxy.NewLocalClientCreator(app) 99 proxyApp := proxy.NewAppConns(cc) 100 err := proxyApp.Start() 101 if err != nil { 102 panic(fmt.Errorf("error start app: %w", err)) 103 } 104 105 blockDB := dbm.NewMemDB() 106 stateDB := dbm.NewMemDB() 107 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 108 DiscardABCIResponses: false, 109 }) 110 blockStore := store.NewBlockStore(blockDB) 111 112 state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) 113 if err != nil { 114 panic(fmt.Errorf("error constructing state from genesis file: %w", err)) 115 } 116 117 // Make the BlockchainReactor itself. 118 // NOTE we have to create and commit the blocks first because 119 // pool.height is determined from the store. 120 fastSync := true 121 db := dbm.NewMemDB() 122 stateStore = sm.NewStore(db, sm.StoreOptions{ 123 DiscardABCIResponses: false, 124 }) 125 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 126 mock.Mempool{}, sm.EmptyEvidencePool{}) 127 if err = stateStore.Save(state); err != nil { 128 panic(err) 129 } 130 131 // let's add some blocks in 132 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 133 lastCommit := types.NewCommit(blockHeight-1, 1, types.BlockID{}, nil) 134 if blockHeight > 1 { 135 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 136 lastBlock := blockStore.LoadBlock(blockHeight - 1) 137 138 vote := makeVote(t, &lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]) 139 lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 140 } 141 142 thisBlock := makeBlock(blockHeight, state, lastCommit) 143 144 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 145 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 146 147 state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) 148 if err != nil { 149 panic(fmt.Errorf("error apply block: %w", err)) 150 } 151 152 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 153 } 154 155 bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) 156 bcReactor.SetLogger(logger.With("module", "blockchain")) 157 158 return bcReactor 159 } 160 161 func newBlockchainReactorPair( 162 t *testing.T, 163 logger log.Logger, 164 genDoc *types.GenesisDoc, 165 privVals []types.PrivValidator, 166 maxBlockHeight int64) BlockchainReactorPair { 167 168 consensusReactor := &consensusReactorTest{} 169 consensusReactor.BaseReactor = *p2p.NewBaseReactor("Consensus reactor", consensusReactor) 170 171 return BlockchainReactorPair{ 172 newBlockchainReactor(t, logger, genDoc, privVals, maxBlockHeight), 173 consensusReactor} 174 } 175 176 type consensusReactorTest struct { 177 p2p.BaseReactor // BaseService + p2p.Switch 178 switchedToConsensus bool 179 mtx sync.Mutex 180 } 181 182 func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced bool) { 183 conR.mtx.Lock() 184 defer conR.mtx.Unlock() 185 conR.switchedToConsensus = true 186 } 187 188 func TestFastSyncNoBlockResponse(t *testing.T) { 189 190 config = cfg.ResetTestRoot("blockchain_new_reactor_test") 191 defer os.RemoveAll(config.RootDir) 192 genDoc, privVals := randGenesisDoc(1, false, 30) 193 194 maxBlockHeight := int64(65) 195 196 reactorPairs := make([]BlockchainReactorPair, 2) 197 198 logger := log.TestingLogger() 199 reactorPairs[0] = newBlockchainReactorPair(t, logger, genDoc, privVals, maxBlockHeight) 200 reactorPairs[1] = newBlockchainReactorPair(t, logger, genDoc, privVals, 0) 201 202 p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { 203 s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR) 204 s.AddReactor("CONSENSUS", reactorPairs[i].conR) 205 moduleName := fmt.Sprintf("blockchain-%v", i) 206 reactorPairs[i].bcR.SetLogger(logger.With("module", moduleName)) 207 208 return s 209 210 }, p2p.Connect2Switches) 211 212 defer func() { 213 for _, r := range reactorPairs { 214 _ = r.bcR.Stop() 215 _ = r.conR.Stop() 216 } 217 }() 218 219 tests := []struct { 220 height int64 221 existent bool 222 }{ 223 {maxBlockHeight + 2, false}, 224 {10, true}, 225 {1, true}, 226 {maxBlockHeight + 100, false}, 227 } 228 229 for { 230 time.Sleep(10 * time.Millisecond) 231 reactorPairs[1].conR.mtx.Lock() 232 if reactorPairs[1].conR.switchedToConsensus { 233 reactorPairs[1].conR.mtx.Unlock() 234 break 235 } 236 reactorPairs[1].conR.mtx.Unlock() 237 } 238 239 assert.Equal(t, maxBlockHeight, reactorPairs[0].bcR.store.Height()) 240 241 for _, tt := range tests { 242 block := reactorPairs[1].bcR.store.LoadBlock(tt.height) 243 if tt.existent { 244 assert.True(t, block != nil) 245 } else { 246 assert.True(t, block == nil) 247 } 248 } 249 } 250 251 // NOTE: This is too hard to test without 252 // an easy way to add test peer to switch 253 // or without significant refactoring of the module. 254 // Alternatively we could actually dial a TCP conn but 255 // that seems extreme. 256 func TestFastSyncBadBlockStopsPeer(t *testing.T) { 257 numNodes := 4 258 maxBlockHeight := int64(148) 259 260 config = cfg.ResetTestRoot("blockchain_reactor_test") 261 defer os.RemoveAll(config.RootDir) 262 genDoc, privVals := randGenesisDoc(1, false, 30) 263 264 otherChain := newBlockchainReactorPair(t, log.TestingLogger(), genDoc, privVals, maxBlockHeight) 265 defer func() { 266 _ = otherChain.bcR.Stop() 267 _ = otherChain.conR.Stop() 268 }() 269 270 reactorPairs := make([]BlockchainReactorPair, numNodes) 271 logger := make([]log.Logger, numNodes) 272 273 for i := 0; i < numNodes; i++ { 274 logger[i] = log.TestingLogger() 275 height := int64(0) 276 if i == 0 { 277 height = maxBlockHeight 278 } 279 reactorPairs[i] = newBlockchainReactorPair(t, logger[i], genDoc, privVals, height) 280 } 281 282 switches := p2p.MakeConnectedSwitches(config.P2P, numNodes, func(i int, s *p2p.Switch) *p2p.Switch { 283 reactorPairs[i].conR.mtx.Lock() 284 s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR) 285 s.AddReactor("CONSENSUS", reactorPairs[i].conR) 286 moduleName := fmt.Sprintf("blockchain-%v", i) 287 reactorPairs[i].bcR.SetLogger(logger[i].With("module", moduleName)) 288 reactorPairs[i].conR.mtx.Unlock() 289 return s 290 291 }, p2p.Connect2Switches) 292 293 defer func() { 294 for _, r := range reactorPairs { 295 _ = r.bcR.Stop() 296 _ = r.conR.Stop() 297 } 298 }() 299 300 outerFor: 301 for { 302 time.Sleep(10 * time.Millisecond) 303 for i := 0; i < numNodes; i++ { 304 reactorPairs[i].conR.mtx.Lock() 305 if !reactorPairs[i].conR.switchedToConsensus { 306 reactorPairs[i].conR.mtx.Unlock() 307 continue outerFor 308 } 309 reactorPairs[i].conR.mtx.Unlock() 310 } 311 break 312 } 313 314 // at this time, reactors[0-3] is the newest 315 assert.Equal(t, numNodes-1, reactorPairs[1].bcR.Switch.Peers().Size()) 316 317 // mark last reactorPair as an invalid peer 318 reactorPairs[numNodes-1].bcR.store = otherChain.bcR.store 319 320 lastLogger := log.TestingLogger() 321 lastReactorPair := newBlockchainReactorPair(t, lastLogger, genDoc, privVals, 0) 322 reactorPairs = append(reactorPairs, lastReactorPair) 323 324 switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { 325 s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].bcR) 326 s.AddReactor("CONSENSUS", reactorPairs[len(reactorPairs)-1].conR) 327 moduleName := fmt.Sprintf("blockchain-%v", len(reactorPairs)-1) 328 reactorPairs[len(reactorPairs)-1].bcR.SetLogger(lastLogger.With("module", moduleName)) 329 return s 330 331 }, p2p.Connect2Switches)...) 332 333 for i := 0; i < len(reactorPairs)-1; i++ { 334 p2p.Connect2Switches(switches, i, len(reactorPairs)-1) 335 } 336 337 for { 338 time.Sleep(1 * time.Second) 339 lastReactorPair.conR.mtx.Lock() 340 if lastReactorPair.conR.switchedToConsensus { 341 lastReactorPair.conR.mtx.Unlock() 342 break 343 } 344 lastReactorPair.conR.mtx.Unlock() 345 346 if lastReactorPair.bcR.Switch.Peers().Size() == 0 { 347 break 348 } 349 } 350 351 assert.True(t, lastReactorPair.bcR.Switch.Peers().Size() < len(reactorPairs)-1) 352 } 353 354 func TestLegacyReactorReceiveBasic(t *testing.T) { 355 config = cfg.ResetTestRoot("blockchain_reactor_test") 356 defer os.RemoveAll(config.RootDir) 357 genDoc, privVals := randGenesisDoc(1, false, 30) 358 reactor := newBlockchainReactor(t, log.TestingLogger(), genDoc, privVals, 10) 359 peer := p2p.CreateRandomPeer(false) 360 361 reactor.InitPeer(peer) 362 reactor.AddPeer(peer) 363 m := &bcproto.StatusRequest{} 364 wm := m.Wrap() 365 msg, err := proto.Marshal(wm) 366 assert.NoError(t, err) 367 368 assert.NotPanics(t, func() { 369 reactor.Receive(BlockchainChannel, peer, msg) 370 }) 371 } 372 373 //---------------------------------------------- 374 // utility funcs 375 376 func makeTxs(height int64) (txs []types.Tx) { 377 for i := 0; i < 10; i++ { 378 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 379 } 380 return txs 381 } 382 383 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 384 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 385 return block 386 } 387 388 type testApp struct { 389 abci.BaseApplication 390 }