You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
8.5 KiB

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