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.

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