github.com/evdatsion/aphelion-dpos-bft@v0.32.1/blockchain/reactor_test.go (about)

     1  package blockchain
     2  
     3  import (
     4  	"os"
     5  	"sort"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	abci "github.com/evdatsion/aphelion-dpos-bft/abci/types"
    12  	cfg "github.com/evdatsion/aphelion-dpos-bft/config"
    13  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    14  	dbm "github.com/evdatsion/aphelion-dpos-bft/libs/db"
    15  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    16  	"github.com/evdatsion/aphelion-dpos-bft/mock"
    17  	"github.com/evdatsion/aphelion-dpos-bft/p2p"
    18  	"github.com/evdatsion/aphelion-dpos-bft/proxy"
    19  	sm "github.com/evdatsion/aphelion-dpos-bft/state"
    20  	"github.com/evdatsion/aphelion-dpos-bft/types"
    21  	tmtime "github.com/evdatsion/aphelion-dpos-bft/types/time"
    22  )
    23  
    24  var config *cfg.Config
    25  
    26  func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
    27  	validators := make([]types.GenesisValidator, numValidators)
    28  	privValidators := make([]types.PrivValidator, numValidators)
    29  	for i := 0; i < numValidators; i++ {
    30  		val, privVal := types.RandValidator(randPower, minPower)
    31  		validators[i] = types.GenesisValidator{
    32  			PubKey: val.PubKey,
    33  			Power:  val.VotingPower,
    34  		}
    35  		privValidators[i] = privVal
    36  	}
    37  	sort.Sort(types.PrivValidatorsByAddress(privValidators))
    38  
    39  	return &types.GenesisDoc{
    40  		GenesisTime: tmtime.Now(),
    41  		ChainID:     config.ChainID(),
    42  		Validators:  validators,
    43  	}, privValidators
    44  }
    45  
    46  func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
    47  	addr := privVal.GetPubKey().Address()
    48  	idx, _ := valset.GetByAddress(addr)
    49  	vote := &types.Vote{
    50  		ValidatorAddress: addr,
    51  		ValidatorIndex:   idx,
    52  		Height:           header.Height,
    53  		Round:            1,
    54  		Timestamp:        tmtime.Now(),
    55  		Type:             types.PrecommitType,
    56  		BlockID:          blockID,
    57  	}
    58  
    59  	privVal.SignVote(header.ChainID, vote)
    60  
    61  	return vote
    62  }
    63  
    64  type BlockchainReactorPair struct {
    65  	reactor *BlockchainReactor
    66  	app     proxy.AppConns
    67  }
    68  
    69  func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) BlockchainReactorPair {
    70  	if len(privVals) != 1 {
    71  		panic("only support one validator")
    72  	}
    73  
    74  	app := &testApp{}
    75  	cc := proxy.NewLocalClientCreator(app)
    76  	proxyApp := proxy.NewAppConns(cc)
    77  	err := proxyApp.Start()
    78  	if err != nil {
    79  		panic(cmn.ErrorWrap(err, "error start app"))
    80  	}
    81  
    82  	blockDB := dbm.NewMemDB()
    83  	stateDB := dbm.NewMemDB()
    84  	blockStore := NewBlockStore(blockDB)
    85  
    86  	state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
    87  	if err != nil {
    88  		panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
    89  	}
    90  
    91  	// Make the BlockchainReactor itself.
    92  	// NOTE we have to create and commit the blocks first because
    93  	// pool.height is determined from the store.
    94  	fastSync := true
    95  	db := dbm.NewMemDB()
    96  	blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
    97  		mock.Mempool{}, sm.MockEvidencePool{})
    98  	sm.SaveState(db, state)
    99  
   100  	// let's add some blocks in
   101  	for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
   102  		lastCommit := types.NewCommit(types.BlockID{}, nil)
   103  		if blockHeight > 1 {
   104  			lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
   105  			lastBlock := blockStore.LoadBlock(blockHeight - 1)
   106  
   107  			vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]).CommitSig()
   108  			lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{vote})
   109  		}
   110  
   111  		thisBlock := makeBlock(blockHeight, state, lastCommit)
   112  
   113  		thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
   114  		blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()}
   115  
   116  		state, err = blockExec.ApplyBlock(state, blockID, thisBlock)
   117  		if err != nil {
   118  			panic(cmn.ErrorWrap(err, "error apply block"))
   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  			r.reactor.Stop()
   151  			r.app.Stop()
   152  		}
   153  	}()
   154  
   155  	tests := []struct {
   156  		height   int64
   157  		existent bool
   158  	}{
   159  		{maxBlockHeight + 2, false},
   160  		{10, true},
   161  		{1, true},
   162  		{100, false},
   163  	}
   164  
   165  	for {
   166  		if reactorPairs[1].reactor.pool.IsCaughtUp() {
   167  			break
   168  		}
   169  
   170  		time.Sleep(10 * time.Millisecond)
   171  	}
   172  
   173  	assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height())
   174  
   175  	for _, tt := range tests {
   176  		block := reactorPairs[1].reactor.store.LoadBlock(tt.height)
   177  		if tt.existent {
   178  			assert.True(t, block != nil)
   179  		} else {
   180  			assert.True(t, block == nil)
   181  		}
   182  	}
   183  }
   184  
   185  // NOTE: This is too hard to test without
   186  // an easy way to add test peer to switch
   187  // or without significant refactoring of the module.
   188  // Alternatively we could actually dial a TCP conn but
   189  // that seems extreme.
   190  func TestBadBlockStopsPeer(t *testing.T) {
   191  	config = cfg.ResetTestRoot("blockchain_reactor_test")
   192  	defer os.RemoveAll(config.RootDir)
   193  	genDoc, privVals := randGenesisDoc(1, false, 30)
   194  
   195  	maxBlockHeight := int64(148)
   196  
   197  	otherChain := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
   198  	defer func() {
   199  		otherChain.reactor.Stop()
   200  		otherChain.app.Stop()
   201  	}()
   202  
   203  	reactorPairs := make([]BlockchainReactorPair, 4)
   204  
   205  	reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
   206  	reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
   207  	reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
   208  	reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
   209  
   210  	switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch {
   211  		s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
   212  		return s
   213  
   214  	}, p2p.Connect2Switches)
   215  
   216  	defer func() {
   217  		for _, r := range reactorPairs {
   218  			r.reactor.Stop()
   219  			r.app.Stop()
   220  		}
   221  	}()
   222  
   223  	for {
   224  		if reactorPairs[3].reactor.pool.IsCaughtUp() {
   225  			break
   226  		}
   227  
   228  		time.Sleep(1 * time.Second)
   229  	}
   230  
   231  	//at this time, reactors[0-3] is the newest
   232  	assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size())
   233  
   234  	//mark reactorPairs[3] is an invalid peer
   235  	reactorPairs[3].reactor.store = otherChain.reactor.store
   236  
   237  	lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
   238  	reactorPairs = append(reactorPairs, lastReactorPair)
   239  
   240  	switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch {
   241  		s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor)
   242  		return s
   243  
   244  	}, p2p.Connect2Switches)...)
   245  
   246  	for i := 0; i < len(reactorPairs)-1; i++ {
   247  		p2p.Connect2Switches(switches, i, len(reactorPairs)-1)
   248  	}
   249  
   250  	for {
   251  		if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 {
   252  			break
   253  		}
   254  
   255  		time.Sleep(1 * time.Second)
   256  	}
   257  
   258  	assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
   259  }
   260  
   261  //----------------------------------------------
   262  // utility funcs
   263  
   264  func makeTxs(height int64) (txs []types.Tx) {
   265  	for i := 0; i < 10; i++ {
   266  		txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
   267  	}
   268  	return txs
   269  }
   270  
   271  func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
   272  	block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
   273  	return block
   274  }
   275  
   276  type testApp struct {
   277  	abci.BaseApplication
   278  }
   279  
   280  var _ abci.Application = (*testApp)(nil)
   281  
   282  func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
   283  	return abci.ResponseInfo{}
   284  }
   285  
   286  func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
   287  	return abci.ResponseBeginBlock{}
   288  }
   289  
   290  func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
   291  	return abci.ResponseEndBlock{}
   292  }
   293  
   294  func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
   295  	return abci.ResponseDeliverTx{Events: []abci.Event{}}
   296  }
   297  
   298  func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
   299  	return abci.ResponseCheckTx{}
   300  }
   301  
   302  func (app *testApp) Commit() abci.ResponseCommit {
   303  	return abci.ResponseCommit{}
   304  }
   305  
   306  func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
   307  	return
   308  }