|
|
- package proxy
-
- import (
- "bytes"
- "fmt"
- "sync"
-
- . "github.com/tendermint/go-common"
- cfg "github.com/tendermint/go-config"
- "github.com/tendermint/tendermint/types" // ...
- tmspcli "github.com/tendermint/tmsp/client"
- "github.com/tendermint/tmsp/example/dummy"
- nilapp "github.com/tendermint/tmsp/example/nil"
- )
-
- //-----------------------------
-
- // Tendermint's interface to the application consists of multiple connections
- type AppConns interface {
- Service
-
- Mempool() AppConnMempool
- Consensus() AppConnConsensus
- Query() AppConnQuery
- }
-
- func NewAppConns(config cfg.Config, clientCreator ClientCreator, state State, blockStore BlockStore) AppConns {
- return NewMultiAppConn(config, clientCreator, state, blockStore)
- }
-
- // a multiAppConn is made of a few appConns (mempool, consensus, query)
- // and manages their underlying tmsp clients, including the handshake
- // which ensures the app and tendermint are synced.
- // TODO: on app restart, clients must reboot together
- type multiAppConn struct {
- BaseService
-
- config cfg.Config
-
- state State
- blockStore BlockStore
-
- mempoolConn *appConnMempool
- consensusConn *appConnConsensus
- queryConn *appConnQuery
-
- clientCreator ClientCreator
- }
-
- // Make all necessary tmsp connections to the application
- func NewMultiAppConn(config cfg.Config, clientCreator ClientCreator, state State, blockStore BlockStore) *multiAppConn {
- multiAppConn := &multiAppConn{
- config: config,
- state: state,
- blockStore: blockStore,
- clientCreator: clientCreator,
- }
- multiAppConn.BaseService = *NewBaseService(log, "multiAppConn", multiAppConn)
- return multiAppConn
- }
-
- // Returns the mempool connection
- func (app *multiAppConn) Mempool() AppConnMempool {
- return app.mempoolConn
- }
-
- // Returns the consensus Connection
- func (app *multiAppConn) Consensus() AppConnConsensus {
- return app.consensusConn
- }
-
- // Returns the query Connection
- func (app *multiAppConn) Query() AppConnQuery {
- return app.queryConn
- }
-
- func (app *multiAppConn) OnStart() error {
- app.BaseService.OnStart()
-
- // query connection
- querycli, err := app.clientCreator.NewTMSPClient()
- if err != nil {
- return err
- }
- app.queryConn = NewAppConnQuery(querycli)
-
- // mempool connection
- memcli, err := app.clientCreator.NewTMSPClient()
- if err != nil {
- return err
- }
- app.mempoolConn = NewAppConnMempool(memcli)
-
- // consensus connection
- concli, err := app.clientCreator.NewTMSPClient()
- if err != nil {
- return err
- }
- app.consensusConn = NewAppConnConsensus(concli)
-
- // ensure app is synced to the latest state
- return app.Handshake()
- }
-
- // TODO: retry the handshake once if it fails the first time
- func (app *multiAppConn) Handshake() error {
- // handshake is done on the query conn
- res, tmspInfo, blockInfo, configInfo := app.queryConn.InfoSync()
- if res.IsErr() {
- return fmt.Errorf("Error calling Info. Code: %v; Data: %X; Log: %s", res.Code, res.Data, res.Log)
- }
-
- if blockInfo == nil {
- log.Warn("blockInfo is nil, aborting handshake")
- return nil
- }
-
- log.Notice("TMSP Handshake", "height", blockInfo.BlockHeight, "block_hash", blockInfo.BlockHash, "app_hash", blockInfo.AppHash)
-
- // TODO: check overflow or change pb to int32
- blockHeight := int(blockInfo.BlockHeight)
- blockHash := blockInfo.BlockHash
- appHash := blockInfo.AppHash
-
- if tmspInfo != nil {
- // TODO: check tmsp version (or do this in the tmspcli?)
- _ = tmspInfo
- }
-
- // of the last block (nil if we starting from 0)
- var header *types.Header
- var partsHeader types.PartSetHeader
-
- // check block
- // if the blockHeight == 0, we will replay everything
- if blockHeight != 0 {
- blockMeta := app.blockStore.LoadBlockMeta(blockHeight)
- if blockMeta == nil {
- return fmt.Errorf("Handshake error. Could not find block #%d", blockHeight)
- }
-
- // check block hash
- if !bytes.Equal(blockMeta.Hash, blockHash) {
- return fmt.Errorf("Handshake error. Block hash at height %d does not match. Got %X, expected %X", blockHeight, blockHash, blockMeta.Hash)
- }
-
- // check app hash
- if !bytes.Equal(blockMeta.Header.AppHash, appHash) {
- return fmt.Errorf("Handshake error. App hash at height %d does not match. Got %X, expected %X", blockHeight, appHash, blockMeta.Header.AppHash)
- }
-
- header = blockMeta.Header
- partsHeader = blockMeta.PartsHeader
- }
-
- if configInfo != nil {
- // TODO: set config info
- _ = configInfo
- }
-
- // replay blocks up to the latest in the blockstore
- err := app.state.ReplayBlocks(header, partsHeader, app.consensusConn, app.blockStore)
- if err != nil {
- return fmt.Errorf("Error on replay: %v", err)
- }
-
- // TODO: (on restart) replay mempool
-
- return nil
- }
-
- //--------------------------------
-
- // Get a connected tmsp client
- func NewTMSPClient(addr, transport string) (tmspcli.Client, error) {
- var client tmspcli.Client
-
- // use local app (for testing)
- // TODO: local proxy app conn
- switch addr {
- case "nilapp":
- app := nilapp.NewNilApplication()
- mtx := new(sync.Mutex) // TODO
- client = tmspcli.NewLocalClient(mtx, app)
- case "dummy":
- app := dummy.NewDummyApplication()
- mtx := new(sync.Mutex) // TODO
- client = tmspcli.NewLocalClient(mtx, app)
- default:
- // Run forever in a loop
- mustConnect := false
- remoteApp, err := tmspcli.NewClient(addr, transport, mustConnect)
- if err != nil {
- return nil, fmt.Errorf("Failed to connect to proxy for mempool: %v", err)
- }
- client = remoteApp
- }
- return client, nil
- }
|