github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/blockchain/v0/reactor_test.go (about)

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