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  }