- package v0
-
- import (
- "fmt"
- "os"
- "sort"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- dbm "github.com/tendermint/tm-db"
-
- abci "github.com/tendermint/tendermint/abci/types"
- cfg "github.com/tendermint/tendermint/config"
- "github.com/tendermint/tendermint/libs/log"
- "github.com/tendermint/tendermint/mempool/mock"
- "github.com/tendermint/tendermint/p2p"
- "github.com/tendermint/tendermint/proxy"
- sm "github.com/tendermint/tendermint/state"
- "github.com/tendermint/tendermint/store"
- "github.com/tendermint/tendermint/types"
- tmtime "github.com/tendermint/tendermint/types/time"
- )
-
- var config *cfg.Config
-
- func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
- validators := make([]types.GenesisValidator, numValidators)
- privValidators := make([]types.PrivValidator, numValidators)
- for i := 0; i < numValidators; i++ {
- val, privVal := types.RandValidator(randPower, minPower)
- validators[i] = types.GenesisValidator{
- PubKey: val.PubKey,
- Power: val.VotingPower,
- }
- privValidators[i] = privVal
- }
- sort.Sort(types.PrivValidatorsByAddress(privValidators))
-
- return &types.GenesisDoc{
- GenesisTime: tmtime.Now(),
- ChainID: config.ChainID(),
- Validators: validators,
- }, privValidators
- }
-
- type BlockchainReactorPair struct {
- reactor *BlockchainReactor
- app proxy.AppConns
- }
-
- func newBlockchainReactor(
- logger log.Logger,
- genDoc *types.GenesisDoc,
- privVals []types.PrivValidator,
- maxBlockHeight int64) BlockchainReactorPair {
- if len(privVals) != 1 {
- panic("only support one validator")
- }
-
- app := &testApp{}
- cc := proxy.NewLocalClientCreator(app)
- proxyApp := proxy.NewAppConns(cc)
- err := proxyApp.Start()
- if err != nil {
- panic(fmt.Errorf("error start app: %w", err))
- }
-
- blockDB := dbm.NewMemDB()
- stateDB := dbm.NewMemDB()
- blockStore := store.NewBlockStore(blockDB)
-
- state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
- if err != nil {
- panic(fmt.Errorf("error constructing state from genesis file: %w", err))
- }
-
- // Make the BlockchainReactor itself.
- // NOTE we have to create and commit the blocks first because
- // pool.height is determined from the store.
- fastSync := true
- db := dbm.NewMemDB()
- blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
- mock.Mempool{}, sm.MockEvidencePool{})
- sm.SaveState(db, state)
-
- // let's add some blocks in
- for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
- lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
- if blockHeight > 1 {
- lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
- lastBlock := blockStore.LoadBlock(blockHeight - 1)
-
- vote, err := types.MakeVote(
- lastBlock.Header.Height,
- lastBlockMeta.BlockID,
- state.Validators,
- privVals[0],
- lastBlock.Header.ChainID,
- time.Now(),
- )
- if err != nil {
- panic(err)
- }
- lastCommit = types.NewCommit(vote.Height, vote.Round,
- lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
- }
-
- thisBlock := makeBlock(blockHeight, state, lastCommit)
-
- thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
- blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
-
- state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
- if err != nil {
- panic(fmt.Errorf("error apply block: %w", err))
- }
-
- blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
- }
-
- bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
- bcReactor.SetLogger(logger.With("module", "blockchain"))
-
- return BlockchainReactorPair{bcReactor, proxyApp}
- }
-
- func TestNoBlockResponse(t *testing.T) {
- config = cfg.ResetTestRoot("blockchain_reactor_test")
- defer os.RemoveAll(config.RootDir)
- genDoc, privVals := randGenesisDoc(1, false, 30)
-
- maxBlockHeight := int64(65)
-
- reactorPairs := make([]BlockchainReactorPair, 2)
-
- reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
- reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
-
- p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch {
- s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
- return s
-
- }, p2p.Connect2Switches)
-
- defer func() {
- for _, r := range reactorPairs {
- err := r.reactor.Stop()
- require.NoError(t, err)
- err = r.app.Stop()
- require.NoError(t, err)
- }
- }()
-
- tests := []struct {
- height int64
- existent bool
- }{
- {maxBlockHeight + 2, false},
- {10, true},
- {1, true},
- {100, false},
- }
-
- for {
- if reactorPairs[1].reactor.pool.IsCaughtUp() {
- break
- }
-
- time.Sleep(10 * time.Millisecond)
- }
-
- assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height())
-
- for _, tt := range tests {
- block := reactorPairs[1].reactor.store.LoadBlock(tt.height)
- if tt.existent {
- assert.True(t, block != nil)
- } else {
- assert.True(t, block == nil)
- }
- }
- }
-
- // NOTE: This is too hard to test without
- // an easy way to add test peer to switch
- // or without significant refactoring of the module.
- // Alternatively we could actually dial a TCP conn but
- // that seems extreme.
- func TestBadBlockStopsPeer(t *testing.T) {
- config = cfg.ResetTestRoot("blockchain_reactor_test")
- defer os.RemoveAll(config.RootDir)
- genDoc, privVals := randGenesisDoc(1, false, 30)
-
- maxBlockHeight := int64(148)
-
- // Other chain needs a different validator set
- otherGenDoc, otherPrivVals := randGenesisDoc(1, false, 30)
- otherChain := newBlockchainReactor(log.TestingLogger(), otherGenDoc, otherPrivVals, maxBlockHeight)
-
- defer func() {
- err := otherChain.reactor.Stop()
- require.Error(t, err)
- err = otherChain.app.Stop()
- require.NoError(t, err)
- }()
-
- reactorPairs := make([]BlockchainReactorPair, 4)
-
- reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
- reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
- reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
- reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
-
- switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch {
- s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
- return s
-
- }, p2p.Connect2Switches)
-
- defer func() {
- for _, r := range reactorPairs {
- err := r.reactor.Stop()
- require.NoError(t, err)
-
- err = r.app.Stop()
- require.NoError(t, err)
- }
- }()
-
- for {
- time.Sleep(1 * time.Second)
- caughtUp := true
- for _, r := range reactorPairs {
- if !r.reactor.pool.IsCaughtUp() {
- caughtUp = false
- }
- }
- if caughtUp {
- break
- }
- }
-
- //at this time, reactors[0-3] is the newest
- assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size())
-
- // Mark reactorPairs[3] as an invalid peer. Fiddling with .store without a mutex is a data
- // race, but can't be easily avoided.
- reactorPairs[3].reactor.store = otherChain.reactor.store
-
- lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
- reactorPairs = append(reactorPairs, lastReactorPair)
-
- switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch {
- s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor)
- return s
-
- }, p2p.Connect2Switches)...)
-
- for i := 0; i < len(reactorPairs)-1; i++ {
- p2p.Connect2Switches(switches, i, len(reactorPairs)-1)
- }
-
- for {
- if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 {
- break
- }
-
- time.Sleep(1 * time.Second)
- }
-
- assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
- }
-
- //----------------------------------------------
- // utility funcs
-
- func makeTxs(height int64) (txs []types.Tx) {
- for i := 0; i < 10; i++ {
- txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
- }
- return txs
- }
-
- func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
- block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
- return block
- }
-
- type testApp struct {
- abci.BaseApplication
- }
-
- var _ abci.Application = (*testApp)(nil)
-
- func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
- return abci.ResponseInfo{}
- }
-
- func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
- return abci.ResponseBeginBlock{}
- }
-
- func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
- return abci.ResponseEndBlock{}
- }
-
- func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
- return abci.ResponseDeliverTx{Events: []abci.Event{}}
- }
-
- func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
- return abci.ResponseCheckTx{}
- }
-
- func (app *testApp) Commit() abci.ResponseCommit {
- return abci.ResponseCommit{}
- }
-
- func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
- return
- }
|