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