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.

199 lines
5.2 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package proxy
  2. import (
  3. "bytes"
  4. "fmt"
  5. "sync"
  6. . "github.com/tendermint/go-common"
  7. cfg "github.com/tendermint/go-config"
  8. "github.com/tendermint/tendermint/types" // ...
  9. tmspcli "github.com/tendermint/tmsp/client"
  10. "github.com/tendermint/tmsp/example/dummy"
  11. nilapp "github.com/tendermint/tmsp/example/nil"
  12. )
  13. //-----------------------------
  14. // Tendermint's interface to the application consists of multiple connections
  15. type AppConns interface {
  16. Service
  17. Mempool() AppConnMempool
  18. Consensus() AppConnConsensus
  19. Query() AppConnQuery
  20. }
  21. func NewAppConns(config cfg.Config, clientCreator ClientCreator, state State, blockStore BlockStore) AppConns {
  22. return NewMultiAppConn(config, clientCreator, state, blockStore)
  23. }
  24. // a multiAppConn is made of a few appConns (mempool, consensus, query)
  25. // and manages their underlying tmsp clients, including the handshake
  26. // which ensures the app and tendermint are synced.
  27. // TODO: on app restart, clients must reboot together
  28. type multiAppConn struct {
  29. BaseService
  30. config cfg.Config
  31. state State
  32. blockStore BlockStore
  33. mempoolConn *appConnMempool
  34. consensusConn *appConnConsensus
  35. queryConn *appConnQuery
  36. clientCreator ClientCreator
  37. }
  38. // Make all necessary tmsp connections to the application
  39. func NewMultiAppConn(config cfg.Config, clientCreator ClientCreator, state State, blockStore BlockStore) *multiAppConn {
  40. multiAppConn := &multiAppConn{
  41. config: config,
  42. state: state,
  43. blockStore: blockStore,
  44. clientCreator: clientCreator,
  45. }
  46. multiAppConn.BaseService = *NewBaseService(log, "multiAppConn", multiAppConn)
  47. return multiAppConn
  48. }
  49. // Returns the mempool connection
  50. func (app *multiAppConn) Mempool() AppConnMempool {
  51. return app.mempoolConn
  52. }
  53. // Returns the consensus Connection
  54. func (app *multiAppConn) Consensus() AppConnConsensus {
  55. return app.consensusConn
  56. }
  57. // Returns the query Connection
  58. func (app *multiAppConn) Query() AppConnQuery {
  59. return app.queryConn
  60. }
  61. func (app *multiAppConn) OnStart() error {
  62. app.BaseService.OnStart()
  63. // query connection
  64. querycli, err := app.clientCreator.NewTMSPClient()
  65. if err != nil {
  66. return err
  67. }
  68. app.queryConn = NewAppConnQuery(querycli)
  69. // mempool connection
  70. memcli, err := app.clientCreator.NewTMSPClient()
  71. if err != nil {
  72. return err
  73. }
  74. app.mempoolConn = NewAppConnMempool(memcli)
  75. // consensus connection
  76. concli, err := app.clientCreator.NewTMSPClient()
  77. if err != nil {
  78. return err
  79. }
  80. app.consensusConn = NewAppConnConsensus(concli)
  81. // ensure app is synced to the latest state
  82. return app.Handshake()
  83. }
  84. // TODO: retry the handshake once if it fails the first time
  85. func (app *multiAppConn) Handshake() error {
  86. // handshake is done on the query conn
  87. res, tmspInfo, blockInfo, configInfo := app.queryConn.InfoSync()
  88. if res.IsErr() {
  89. return fmt.Errorf("Error calling Info. Code: %v; Data: %X; Log: %s", res.Code, res.Data, res.Log)
  90. }
  91. if blockInfo == nil {
  92. log.Warn("blockInfo is nil, aborting handshake")
  93. return nil
  94. }
  95. log.Notice("TMSP Handshake", "height", blockInfo.BlockHeight, "block_hash", blockInfo.BlockHash, "app_hash", blockInfo.AppHash)
  96. // TODO: check overflow or change pb to int32
  97. blockHeight := int(blockInfo.BlockHeight)
  98. blockHash := blockInfo.BlockHash
  99. appHash := blockInfo.AppHash
  100. if tmspInfo != nil {
  101. // TODO: check tmsp version (or do this in the tmspcli?)
  102. _ = tmspInfo
  103. }
  104. // of the last block (nil if we starting from 0)
  105. var header *types.Header
  106. var partsHeader types.PartSetHeader
  107. // check block
  108. // if the blockHeight == 0, we will replay everything
  109. if blockHeight != 0 {
  110. blockMeta := app.blockStore.LoadBlockMeta(blockHeight)
  111. if blockMeta == nil {
  112. return fmt.Errorf("Handshake error. Could not find block #%d", blockHeight)
  113. }
  114. // check block hash
  115. if !bytes.Equal(blockMeta.Hash, blockHash) {
  116. return fmt.Errorf("Handshake error. Block hash at height %d does not match. Got %X, expected %X", blockHeight, blockHash, blockMeta.Hash)
  117. }
  118. // check app hash
  119. if !bytes.Equal(blockMeta.Header.AppHash, appHash) {
  120. return fmt.Errorf("Handshake error. App hash at height %d does not match. Got %X, expected %X", blockHeight, appHash, blockMeta.Header.AppHash)
  121. }
  122. header = blockMeta.Header
  123. partsHeader = blockMeta.PartsHeader
  124. }
  125. if configInfo != nil {
  126. // TODO: set config info
  127. _ = configInfo
  128. }
  129. // replay blocks up to the latest in the blockstore
  130. err := app.state.ReplayBlocks(header, partsHeader, app.consensusConn, app.blockStore)
  131. if err != nil {
  132. return fmt.Errorf("Error on replay: %v", err)
  133. }
  134. // TODO: (on restart) replay mempool
  135. return nil
  136. }
  137. //--------------------------------
  138. // Get a connected tmsp client
  139. func NewTMSPClient(addr, transport string) (tmspcli.Client, error) {
  140. var client tmspcli.Client
  141. // use local app (for testing)
  142. // TODO: local proxy app conn
  143. switch addr {
  144. case "nilapp":
  145. app := nilapp.NewNilApplication()
  146. mtx := new(sync.Mutex) // TODO
  147. client = tmspcli.NewLocalClient(mtx, app)
  148. case "dummy":
  149. app := dummy.NewDummyApplication()
  150. mtx := new(sync.Mutex) // TODO
  151. client = tmspcli.NewLocalClient(mtx, app)
  152. default:
  153. // Run forever in a loop
  154. mustConnect := false
  155. remoteApp, err := tmspcli.NewClient(addr, transport, mustConnect)
  156. if err != nil {
  157. return nil, fmt.Errorf("Failed to connect to proxy for mempool: %v", err)
  158. }
  159. client = remoteApp
  160. }
  161. return client, nil
  162. }