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.

302 lines
8.4 KiB

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