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