proxy: typed app conns

This commit is contained in:
Ethan Buchman
2016-08-17 22:28:08 -04:00
parent 4802145e8f
commit 3a7ee13ece
12 changed files with 319 additions and 100 deletions

View File

@@ -2,8 +2,135 @@ package proxy
import (
tmspcli "github.com/tendermint/tmsp/client"
"github.com/tendermint/tmsp/types"
)
type AppConn interface {
tmspcli.Client
//----------------------------------------------------------------------------------------
// Enforce which tmsp msgs can be sent on a connection at the type level
type AppConnConsensus interface {
SetResponseCallback(tmspcli.Callback)
Error() error
InitChainSync(validators []*types.Validator) (err error)
BeginBlockSync(height uint64) (err error)
AppendTxAsync(tx []byte) *tmspcli.ReqRes
EndBlockSync(height uint64) (changedValidators []*types.Validator, err error)
CommitSync() (res types.Result)
}
type AppConnMempool interface {
SetResponseCallback(tmspcli.Callback)
Error() error
CheckTxAsync(tx []byte) *tmspcli.ReqRes
FlushAsync() *tmspcli.ReqRes
FlushSync() error
}
type AppConnQuery interface {
Error() error
EchoSync(string) (res types.Result)
InfoSync() (res types.Result)
QuerySync(tx []byte) (res types.Result)
// SetOptionSync(key string, value string) (res types.Result)
}
//-----------------------------------------------------------------------------------------
// Implements AppConnConsensus (subset of tmspcli.Client)
type appConnConsensus struct {
appConn tmspcli.Client
}
func NewAppConnConsensus(appConn tmspcli.Client) *appConnConsensus {
return &appConnConsensus{
appConn: appConn,
}
}
func (app *appConnConsensus) SetResponseCallback(cb tmspcli.Callback) {
app.appConn.SetResponseCallback(cb)
}
func (app *appConnConsensus) Error() error {
return app.appConn.Error()
}
func (app *appConnConsensus) InitChainSync(validators []*types.Validator) (err error) {
return app.appConn.InitChainSync(validators)
}
func (app *appConnConsensus) BeginBlockSync(height uint64) (err error) {
return app.appConn.BeginBlockSync(height)
}
func (app *appConnConsensus) AppendTxAsync(tx []byte) *tmspcli.ReqRes {
return app.appConn.AppendTxAsync(tx)
}
func (app *appConnConsensus) EndBlockSync(height uint64) (changedValidators []*types.Validator, err error) {
return app.appConn.EndBlockSync(height)
}
func (app *appConnConsensus) CommitSync() (res types.Result) {
return app.appConn.CommitSync()
}
//------------------------------------------------
// Implements AppConnMempool (subset of tmspcli.Client)
type appConnMempool struct {
appConn tmspcli.Client
}
func NewAppConnMempool(appConn tmspcli.Client) *appConnMempool {
return &appConnMempool{
appConn: appConn,
}
}
func (app *appConnMempool) SetResponseCallback(cb tmspcli.Callback) {
app.appConn.SetResponseCallback(cb)
}
func (app *appConnMempool) Error() error {
return app.appConn.Error()
}
func (app *appConnMempool) FlushAsync() *tmspcli.ReqRes {
return app.appConn.FlushAsync()
}
func (app *appConnMempool) FlushSync() error {
return app.appConn.FlushSync()
}
func (app *appConnMempool) CheckTxAsync(tx []byte) *tmspcli.ReqRes {
return app.appConn.CheckTxAsync(tx)
}
//------------------------------------------------
// Implements AppConnQuery (subset of tmspcli.Client)
type appConnQuery struct {
appConn tmspcli.Client
}
func NewAppConnQuery(appConn tmspcli.Client) *appConnQuery {
return &appConnQuery{
appConn: appConn,
}
}
func (app *appConnQuery) Error() error {
return app.appConn.Error()
}
func (app *appConnQuery) InfoSync() (res types.Result) {
return app.appConn.InfoSync()
}
func (app *appConnQuery) QuerySync(tx []byte) (res types.Result) {
return app.appConn.QuerySync(tx)
}

View File

@@ -5,10 +5,42 @@ import (
"testing"
. "github.com/tendermint/go-common"
tmspcli "github.com/tendermint/tmsp/client"
"github.com/tendermint/tmsp/example/dummy"
"github.com/tendermint/tmsp/server"
"github.com/tendermint/tmsp/types"
)
//----------------------------------------
type AppConnTest interface {
EchoAsync(string) *tmspcli.ReqRes
FlushSync() error
InfoSync() (res types.Result)
}
type appConnTest struct {
appConn tmspcli.Client
}
func NewAppConnTest(appConn tmspcli.Client) AppConnTest {
return &appConnTest{appConn}
}
func (app *appConnTest) EchoAsync(msg string) *tmspcli.ReqRes {
return app.appConn.EchoAsync(msg)
}
func (app *appConnTest) FlushSync() error {
return app.appConn.FlushSync()
}
func (app *appConnTest) InfoSync() types.Result {
return app.appConn.InfoSync()
}
//----------------------------------------
var SOCKET = "socket"
func TestEcho(t *testing.T) {
@@ -21,12 +53,12 @@ func TestEcho(t *testing.T) {
}
defer s.Stop()
// Start client
proxy, err := NewRemoteAppConn(sockPath, SOCKET)
cli, err := NewTMSPClient(sockPath, SOCKET)
if err != nil {
Exit(err.Error())
} else {
t.Log("Connected")
}
proxy := NewAppConnTest(cli)
t.Log("Connected")
for i := 0; i < 1000; i++ {
proxy.EchoAsync(Fmt("echo-%v", i))
@@ -44,12 +76,12 @@ func BenchmarkEcho(b *testing.B) {
}
defer s.Stop()
// Start client
proxy, err := NewRemoteAppConn(sockPath, SOCKET)
cli, err := NewTMSPClient(sockPath, SOCKET)
if err != nil {
Exit(err.Error())
} else {
b.Log("Connected")
}
proxy := NewAppConnTest(cli)
b.Log("Connected")
echoString := strings.Repeat(" ", 200)
b.StartTimer() // Start benchmarking tests
@@ -72,12 +104,13 @@ func TestInfo(t *testing.T) {
}
defer s.Stop()
// Start client
proxy, err := NewRemoteAppConn(sockPath, SOCKET)
cli, err := NewTMSPClient(sockPath, SOCKET)
if err != nil {
Exit(err.Error())
} else {
t.Log("Connected")
}
proxy := NewAppConnTest(cli)
t.Log("Connected")
res := proxy.InfoSync()
if res.IsErr() {
t.Errorf("Unexpected error: %v", err)

116
proxy/multi_app_conn.go Normal file
View File

@@ -0,0 +1,116 @@
package proxy
import (
"fmt"
"sync"
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
tmspcli "github.com/tendermint/tmsp/client"
"github.com/tendermint/tmsp/example/dummy"
nilapp "github.com/tendermint/tmsp/example/nil"
)
// Get a connected tmsp client and perform handshake
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
}
// TODO
func Handshake(config cfg.Config, state State, blockStore BlockStore) {
// XXX: Handshake
/*res := client.CommitSync()
if res.IsErr() {
PanicCrisis(Fmt("Error in getting multiAppConnConn hash: %v", res))
}
if !bytes.Equal(hash, res.Data) {
log.Warn(Fmt("ProxyApp hash does not match. Expected %X, got %X", hash, res.Data))
}*/
}
//---------
// a multiAppConn is made of a few appConns (mempool, consensus)
// and manages their underlying tmsp clients, ensuring they reboot together
type multiAppConn struct {
QuitService
config cfg.Config
state State
blockStore BlockStore
mempoolConn *appConnMempool
consensusConn *appConnConsensus
}
// Make all necessary tmsp connections to the application
func NewMultiAppConn(config cfg.Config, state State, blockStore BlockStore) *multiAppConn {
multiAppConn := &multiAppConn{
config: config,
state: state,
blockStore: blockStore,
}
multiAppConn.QuitService = *NewQuitService(log, "multiAppConn", multiAppConn)
multiAppConn.Start()
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
}
func (app *multiAppConn) OnStart() error {
app.QuitService.OnStart()
addr := app.config.GetString("proxy_app")
transport := app.config.GetString("tmsp")
memcli, err := NewTMSPClient(addr, transport)
if err != nil {
return err
}
app.mempoolConn = NewAppConnMempool(memcli)
concli, err := NewTMSPClient(addr, transport)
if err != nil {
return err
}
app.consensusConn = NewAppConnConsensus(concli)
// TODO: handshake
// TODO: replay blocks
// TODO: (on restart) replay mempool
return nil
}

View File

@@ -1,23 +0,0 @@
package proxy
import (
tmspcli "github.com/tendermint/tmsp/client"
)
// This is goroutine-safe, but users should beware that
// the application in general is not meant to be interfaced
// with concurrent callers.
type remoteAppConn struct {
tmspcli.Client
}
func NewRemoteAppConn(addr, transport string) (*remoteAppConn, error) {
client, err := tmspcli.NewClient(addr, transport, false)
if err != nil {
return nil, err
}
appConn := &remoteAppConn{
Client: client,
}
return appConn, nil
}

9
proxy/state.go Normal file
View File

@@ -0,0 +1,9 @@
package proxy
type State interface {
// TODO
}
type BlockStore interface {
// TODO
}