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