github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/blockchain/v0/reactor_test.go (about)

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