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