ws fixes; rpc tests
This commit is contained in:
parent
790cde028b
commit
96547d0ca8
|
@ -30,6 +30,7 @@ func initTMRoot(rootDir string) {
|
||||||
|
|
||||||
configFilePath := path.Join(rootDir, "config.toml")
|
configFilePath := path.Join(rootDir, "config.toml")
|
||||||
genesisFilePath := path.Join(rootDir, "genesis.json")
|
genesisFilePath := path.Join(rootDir, "genesis.json")
|
||||||
|
privFilePath := path.Join(rootDir, "priv_validator.json")
|
||||||
|
|
||||||
// Write default config file if missing.
|
// Write default config file if missing.
|
||||||
if !FileExists(configFilePath) {
|
if !FileExists(configFilePath) {
|
||||||
|
@ -40,6 +41,9 @@ func initTMRoot(rootDir string) {
|
||||||
if !FileExists(genesisFilePath) {
|
if !FileExists(genesisFilePath) {
|
||||||
MustWriteFile(genesisFilePath, []byte(defaultGenesis), 0644)
|
MustWriteFile(genesisFilePath, []byte(defaultGenesis), 0644)
|
||||||
}
|
}
|
||||||
|
if !FileExists(privFilePath) {
|
||||||
|
MustWriteFile(privFilePath, []byte(defaultPrivValidator), 0644)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfig(rootDir string) cfg.Config {
|
func GetConfig(rootDir string) cfg.Config {
|
||||||
|
@ -58,7 +62,7 @@ func GetConfig(rootDir string) cfg.Config {
|
||||||
}
|
}
|
||||||
mapConfig.SetDefault("chain_id", "tendermint_test")
|
mapConfig.SetDefault("chain_id", "tendermint_test")
|
||||||
mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json")
|
mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json")
|
||||||
mapConfig.SetDefault("proxy_app", "tcp://127.0.0.1:36658")
|
mapConfig.SetDefault("proxy_app", "local")
|
||||||
mapConfig.SetDefault("moniker", "anonymous")
|
mapConfig.SetDefault("moniker", "anonymous")
|
||||||
mapConfig.SetDefault("node_laddr", "0.0.0.0:36656")
|
mapConfig.SetDefault("node_laddr", "0.0.0.0:36656")
|
||||||
mapConfig.SetDefault("fast_sync", false)
|
mapConfig.SetDefault("fast_sync", false)
|
||||||
|
@ -78,7 +82,7 @@ func GetConfig(rootDir string) cfg.Config {
|
||||||
var defaultConfigTmpl = `# This is a TOML config file.
|
var defaultConfigTmpl = `# This is a TOML config file.
|
||||||
# For more information, see https://github.com/toml-lang/toml
|
# For more information, see https://github.com/toml-lang/toml
|
||||||
|
|
||||||
proxy_app = "tcp://127.0.0.1:36658"
|
proxy_app = "local"
|
||||||
moniker = "__MONIKER__"
|
moniker = "__MONIKER__"
|
||||||
node_laddr = "0.0.0.0:36656"
|
node_laddr = "0.0.0.0:36656"
|
||||||
seeds = ""
|
seeds = ""
|
||||||
|
@ -93,49 +97,32 @@ func defaultConfig(moniker string) (defaultConfig string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// priv keys generated deterministically eg rpc/tests/helpers.go
|
|
||||||
var defaultGenesis = `{
|
var defaultGenesis = `{
|
||||||
|
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||||
"chain_id": "tendermint_test",
|
"chain_id": "tendermint_test",
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"address": "E9B5D87313356465FAE33C406CE2C2979DE60BCB",
|
|
||||||
"amount": 200000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "DFE4AFFA4CEE17CD01CB9E061D77C3ECED29BD88",
|
|
||||||
"amount": 200000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "F60D30722E7B497FA532FB3207C3FB29C31B1992",
|
|
||||||
"amount": 200000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "336CB40A5EB92E496E19B74FDFF2BA017C877FD6",
|
|
||||||
"amount": 200000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "D218F0F439BF0384F6F5EF8D0F8B398D941BD1DC",
|
|
||||||
"amount": 200000000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"validators": [
|
"validators": [
|
||||||
{
|
{
|
||||||
"pub_key": [1, "583779C3BFA3F6C7E23C7D830A9C3D023A216B55079AD38BFED1207B94A19548"],
|
"pub_key": [
|
||||||
"amount": 1000000,
|
1,
|
||||||
"unbond_to": [
|
"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
||||||
{
|
],
|
||||||
"address": "E9B5D87313356465FAE33C406CE2C2979DE60BCB",
|
"amount": 10,
|
||||||
"amount": 100000
|
"name": ""
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"app_hash": ""
|
||||||
]
|
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var defaultPrivValidator = `{
|
var defaultPrivValidator = `{
|
||||||
"address": "1D7A91CB32F758A02EBB9BE1FB6F8DEE56F90D42",
|
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
|
||||||
"pub_key": [1,"06FBAC4E285285D1D91FCBC7E91C780ADA11516F67462340B3980CE2B94940E8"],
|
"pub_key": [
|
||||||
"priv_key": [1,"C453604BD6480D5538B4C6FD2E3E314B5BCE518D75ADE4DA3DA85AB8ADFD819606FBAC4E285285D1D91FCBC7E91C780ADA11516F67462340B3980CE2B94940E8"],
|
1,
|
||||||
|
"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
||||||
|
],
|
||||||
|
"priv_key": [
|
||||||
|
1,
|
||||||
|
"27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
||||||
|
],
|
||||||
"last_height": 0,
|
"last_height": 0,
|
||||||
"last_round": 0,
|
"last_round": 0,
|
||||||
"last_step": 0
|
"last_step": 0
|
||||||
|
|
15
node/node.go
15
node/node.go
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/tendermint/tendermint/rpc/server"
|
"github.com/tendermint/tendermint/rpc/server"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
"github.com/tendermint/tmsp/example/golang"
|
||||||
)
|
)
|
||||||
|
|
||||||
import _ "net/http/pprof"
|
import _ "net/http/pprof"
|
||||||
|
@ -320,14 +321,22 @@ func getState() *sm.State {
|
||||||
|
|
||||||
// Get a connection to the proxyAppConn addr.
|
// Get a connection to the proxyAppConn addr.
|
||||||
// Check the current hash, and panic if it doesn't match.
|
// Check the current hash, and panic if it doesn't match.
|
||||||
func getProxyApp(addr string, hash []byte) proxy.AppConn {
|
func getProxyApp(addr string, hash []byte) (proxyAppCtx proxy.AppContext) {
|
||||||
|
// use local app (for testing)
|
||||||
|
if addr == "local" {
|
||||||
|
app := example.NewCounterApplication(true)
|
||||||
|
appCtx := app.Open()
|
||||||
|
proxyAppCtx = proxy.NewLocalAppContext(appCtx)
|
||||||
|
} else {
|
||||||
proxyConn, err := Connect(addr)
|
proxyConn, err := Connect(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(Fmt("Failed to connect to proxy for mempool: %v", err))
|
Exit(Fmt("Failed to connect to proxy for mempool: %v", err))
|
||||||
}
|
}
|
||||||
proxyAppConn := proxy.NewRemoteAppConn(proxyConn, 1024)
|
proxyAppCtx := proxy.NewRemoteAppContext(proxyConn, 1024)
|
||||||
|
|
||||||
proxyAppConn.Start()
|
proxyAppCtx.Start()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Check the hash
|
// Check the hash
|
||||||
currentHash, err := proxyAppConn.GetHashSync()
|
currentHash, err := proxyAppConn.GetHashSync()
|
||||||
|
|
|
@ -2,17 +2,47 @@ package rpcclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/tendermint/rpc/types"
|
"github.com/tendermint/tendermint/rpc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CallHTTP(remote string, method string, params []interface{}, dest interface{}) (interface{}, error) {
|
// JSON rpc takes params as a slice
|
||||||
|
type ClientJSONRPC struct {
|
||||||
|
remote string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientJSONRPC(remote string) *ClientJSONRPC {
|
||||||
|
return &ClientJSONRPC{remote}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientJSONRPC) Call(method string, params []interface{}) (interface{}, error) {
|
||||||
|
return CallHTTP_JSONRPC(c.remote, method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// URI takes params as a map
|
||||||
|
type ClientURI struct {
|
||||||
|
remote string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientURI(remote string) *ClientURI {
|
||||||
|
if !strings.HasSuffix(remote, "/") {
|
||||||
|
remote = remote + "/"
|
||||||
|
}
|
||||||
|
return &ClientURI{remote}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientURI) Call(method string, params map[string]interface{}) (interface{}, error) {
|
||||||
|
return CallHTTP_URI(c.remote, method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallHTTP_JSONRPC(remote string, method string, params []interface{}) (interface{}, error) {
|
||||||
// Make request and get responseBytes
|
// Make request and get responseBytes
|
||||||
request := rpctypes.RPCRequest{
|
request := rpctypes.RPCRequest{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
|
@ -25,27 +55,79 @@ func CallHTTP(remote string, method string, params []interface{}, dest interface
|
||||||
log.Info(Fmt("RPC request to %v: %v", remote, string(requestBytes)))
|
log.Info(Fmt("RPC request to %v: %v", remote, string(requestBytes)))
|
||||||
httpResponse, err := http.Post(remote, "text/json", requestBuf)
|
httpResponse, err := http.Post(remote, "text/json", requestBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dest, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer httpResponse.Body.Close()
|
defer httpResponse.Body.Close()
|
||||||
responseBytes, err := ioutil.ReadAll(httpResponse.Body)
|
responseBytes, err := ioutil.ReadAll(httpResponse.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dest, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Info(Fmt("RPC response: %v", string(responseBytes)))
|
log.Info(Fmt("RPC response: %v", string(responseBytes)))
|
||||||
|
return unmarshalResponseBytes(responseBytes)
|
||||||
// Parse response into JSONResponse
|
}
|
||||||
response := rpctypes.RPCResponse{}
|
|
||||||
err = json.Unmarshal(responseBytes, &response)
|
func CallHTTP_URI(remote string, method string, params map[string]interface{}) (interface{}, error) {
|
||||||
if err != nil {
|
values, err := argsToURLValues(params)
|
||||||
return dest, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Info(Fmt("URI request to %v: %v", remote, values))
|
||||||
|
resp, err := http.PostForm(remote+method, values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
responseBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return unmarshalResponseBytes(responseBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
func unmarshalResponseBytes(responseBytes []byte) (interface{}, error) {
|
||||||
|
// read response
|
||||||
|
// if rpc/core/types is imported, the result will unmarshal
|
||||||
|
// into the correct type
|
||||||
|
var err error
|
||||||
|
response := &rpctypes.RPCResponse{}
|
||||||
|
wire.ReadJSON(response, responseBytes, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
// Parse response into dest
|
|
||||||
resultJSONObject := response.Result
|
|
||||||
errorStr := response.Error
|
errorStr := response.Error
|
||||||
if errorStr != "" {
|
if errorStr != "" {
|
||||||
return dest, errors.New(errorStr)
|
return nil, errors.New(errorStr)
|
||||||
}
|
}
|
||||||
dest = wire.ReadJSONObject(dest, resultJSONObject, &err)
|
return response.Result, err
|
||||||
return dest, err
|
}
|
||||||
|
|
||||||
|
func argsToURLValues(args map[string]interface{}) (url.Values, error) {
|
||||||
|
values := make(url.Values)
|
||||||
|
if len(args) == 0 {
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
err := argsToJson(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, val := range args {
|
||||||
|
values.Set(key, val.(string))
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func argsToJson(args map[string]interface{}) error {
|
||||||
|
var n int
|
||||||
|
var err error
|
||||||
|
for k, v := range args {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
wire.WriteJSON(v, buf, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
args[k] = buf.String()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscri
|
||||||
wsCtx.GetEventSwitch().AddListenerForEvent(wsCtx.GetRemoteAddr(), event, func(msg types.EventData) {
|
wsCtx.GetEventSwitch().AddListenerForEvent(wsCtx.GetRemoteAddr(), event, func(msg types.EventData) {
|
||||||
// NOTE: EventSwitch callbacks must be nonblocking
|
// NOTE: EventSwitch callbacks must be nonblocking
|
||||||
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
||||||
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", ctypes.ResultEvent{event, msg}, ""))
|
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &ctypes.ResultEvent{event, msg}, ""))
|
||||||
})
|
})
|
||||||
return &ctypes.ResultSubscribe{}, nil
|
return &ctypes.ResultSubscribe{}, nil
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func Unsubscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultUnsub
|
||||||
wsCtx.GetEventSwitch().AddListenerForEvent(wsCtx.GetRemoteAddr(), event, func(msg types.EventData) {
|
wsCtx.GetEventSwitch().AddListenerForEvent(wsCtx.GetRemoteAddr(), event, func(msg types.EventData) {
|
||||||
// NOTE: EventSwitch callbacks must be nonblocking
|
// NOTE: EventSwitch callbacks must be nonblocking
|
||||||
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
||||||
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", ctypes.ResultEvent{event, msg}, ""))
|
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &ctypes.ResultEvent{event, msg}, ""))
|
||||||
})
|
})
|
||||||
return &ctypes.ResultUnsubscribe{}, nil
|
return &ctypes.ResultUnsubscribe{}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, params []interface{}) ([]reflect.Value,
|
||||||
|
|
||||||
// Same as above, but with the first param the websocket connection
|
// Same as above, but with the first param the websocket connection
|
||||||
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params []interface{}, wsCtx WSRPCContext) ([]reflect.Value, error) {
|
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params []interface{}, wsCtx WSRPCContext) ([]reflect.Value, error) {
|
||||||
if len(rpcFunc.argNames)-1 != len(params) {
|
if len(rpcFunc.argNames) != len(params) {
|
||||||
return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)",
|
return nil, errors.New(fmt.Sprintf("Expected %v parameters (%v), got %v (%v)",
|
||||||
len(rpcFunc.argNames)-1, rpcFunc.argNames[1:], len(params), params))
|
len(rpcFunc.argNames)-1, rpcFunc.argNames[1:], len(params), params))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
package rpctest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "github.com/tendermint/tendermint/config/tendermint_test"
|
||||||
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
// Test the HTTP client
|
||||||
|
|
||||||
|
func TestURIStatus(t *testing.T) {
|
||||||
|
result, err := clientURI.Call("status", map[string]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testStatus(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONStatus(t *testing.T) {
|
||||||
|
result, err := clientJSON.Call("status", []interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testStatus(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStatus(t *testing.T, result interface{}) {
|
||||||
|
status := result.(*ctypes.ResultStatus)
|
||||||
|
if status.NodeInfo.Network != chainID {
|
||||||
|
t.Fatal(fmt.Errorf("ChainID mismatch: got %s expected %s",
|
||||||
|
status.NodeInfo.Network, chainID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func TestURIBroadcastTx(t *testing.T) {
|
||||||
|
testBroadcastTx(t, "HTTP")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*func TestJSONBroadcastTx(t *testing.T) {
|
||||||
|
testBroadcastTx(t, "JSONRPC")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*
|
||||||
|
func testBroadcastTx(t *testing.T, typ string) {
|
||||||
|
amt := int64(100)
|
||||||
|
toAddr := user[1].Address
|
||||||
|
tx := makeDefaultSendTxSigned(t, typ, toAddr, amt)
|
||||||
|
receipt := broadcastTx(t, typ, tx)
|
||||||
|
if receipt.CreatesContract > 0 {
|
||||||
|
t.Fatal("This tx does not create a contract")
|
||||||
|
}
|
||||||
|
if len(receipt.TxHash) == 0 {
|
||||||
|
t.Fatal("Failed to compute tx hash")
|
||||||
|
}
|
||||||
|
pool := node.MempoolReactor().Mempool
|
||||||
|
txs := pool.GetProposalTxs()
|
||||||
|
if len(txs) != mempoolCount {
|
||||||
|
t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount)
|
||||||
|
}
|
||||||
|
tx2 := txs[mempoolCount-1].(*types.SendTx)
|
||||||
|
n, err := new(int64), new(error)
|
||||||
|
buf1, buf2 := new(bytes.Buffer), new(bytes.Buffer)
|
||||||
|
tx.WriteSignBytes(chainID, buf1, n, err)
|
||||||
|
tx2.WriteSignBytes(chainID, buf2, n, err)
|
||||||
|
if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 {
|
||||||
|
t.Fatal("inconsistent hashes for mempool tx and sent tx")
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
// Test the websocket service
|
||||||
|
|
||||||
|
var wsTyp = "JSONRPC"
|
||||||
|
|
||||||
|
// make a simple connection to the server
|
||||||
|
func TestWSConnect(t *testing.T) {
|
||||||
|
con := newWSCon(t)
|
||||||
|
con.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive a new block message
|
||||||
|
func TestWSNewBlock(t *testing.T) {
|
||||||
|
con := newWSCon(t)
|
||||||
|
eid := types.EventStringNewBlock()
|
||||||
|
subscribe(t, con, eid)
|
||||||
|
defer func() {
|
||||||
|
unsubscribe(t, con, eid)
|
||||||
|
con.Close()
|
||||||
|
}()
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error {
|
||||||
|
fmt.Println("Check:", string(b))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive a few new block messages in a row, with increasing height
|
||||||
|
func TestWSBlockchainGrowth(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode.")
|
||||||
|
}
|
||||||
|
con := newWSCon(t)
|
||||||
|
eid := types.EventStringNewBlock()
|
||||||
|
subscribe(t, con, eid)
|
||||||
|
defer func() {
|
||||||
|
unsubscribe(t, con, eid)
|
||||||
|
con.Close()
|
||||||
|
}()
|
||||||
|
// listen for NewBlock, ensure height increases by 1
|
||||||
|
unmarshalValidateBlockchain(t, con, eid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: this with dummy app..
|
||||||
|
func TestWSDoubleFire(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode.")
|
||||||
|
}
|
||||||
|
con := newWSCon(t)
|
||||||
|
eid := types.EventStringAccInput(user[0].Address)
|
||||||
|
subscribe(t, con, eid)
|
||||||
|
defer func() {
|
||||||
|
unsubscribe(t, con, eid)
|
||||||
|
con.Close()
|
||||||
|
}()
|
||||||
|
amt := int64(100)
|
||||||
|
toAddr := user[1].Address
|
||||||
|
// broadcast the transaction, wait to hear about it
|
||||||
|
waitForEvent(t, con, eid, true, func() {
|
||||||
|
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
|
||||||
|
broadcastTx(t, wsTyp, tx)
|
||||||
|
}, func(eid string, b []byte) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// but make sure we don't hear about it twice
|
||||||
|
waitForEvent(t, con, eid, false, func() {
|
||||||
|
}, func(eid string, b []byte) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}*/
|
|
@ -0,0 +1,18 @@
|
||||||
|
package rpctest
|
||||||
|
|
||||||
|
import (
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
tmcfg "github.com/tendermint/tendermint/config/tendermint_test"
|
||||||
|
)
|
||||||
|
|
||||||
|
var config cfg.Config = nil
|
||||||
|
|
||||||
|
func initConfig() {
|
||||||
|
|
||||||
|
cfg.OnConfig(func(newConfig cfg.Config) {
|
||||||
|
config = newConfig
|
||||||
|
})
|
||||||
|
|
||||||
|
c := tmcfg.GetConfig("")
|
||||||
|
cfg.ApplyConfig(c) // Notify modules of new config
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package rpctest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
. "github.com/tendermint/go-common"
|
||||||
|
"github.com/tendermint/go-p2p"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
|
||||||
|
_ "github.com/tendermint/tendermint/config/tendermint_test"
|
||||||
|
nm "github.com/tendermint/tendermint/node"
|
||||||
|
client "github.com/tendermint/tendermint/rpc/client"
|
||||||
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
"github.com/tendermint/tendermint/rpc/types"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// global variables for use across all tests
|
||||||
|
var (
|
||||||
|
rpcAddr = "127.0.0.1:36657" // Not 46657
|
||||||
|
requestAddr = "http://" + rpcAddr
|
||||||
|
websocketAddr = "ws://" + rpcAddr + "/websocket"
|
||||||
|
|
||||||
|
node *nm.Node
|
||||||
|
|
||||||
|
mempoolCount = 0
|
||||||
|
|
||||||
|
chainID string
|
||||||
|
|
||||||
|
clientURI = client.NewClientURI(requestAddr)
|
||||||
|
clientJSON = client.NewClientJSONRPC(requestAddr)
|
||||||
|
)
|
||||||
|
|
||||||
|
// create a new node and sleep forever
|
||||||
|
func newNode(ready chan struct{}) {
|
||||||
|
// Create & start node
|
||||||
|
node = nm.NewNode()
|
||||||
|
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), true)
|
||||||
|
node.AddListener(l)
|
||||||
|
node.Start()
|
||||||
|
|
||||||
|
// Run the RPC server.
|
||||||
|
node.StartRPC()
|
||||||
|
ready <- struct{}{}
|
||||||
|
|
||||||
|
// Sleep forever
|
||||||
|
ch := make(chan struct{})
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize config and create new node
|
||||||
|
func init() {
|
||||||
|
initConfig()
|
||||||
|
chainID = config.GetString("chain_id")
|
||||||
|
|
||||||
|
// TODO: change consensus/state.go timeouts to be shorter
|
||||||
|
|
||||||
|
// start a node
|
||||||
|
ready := make(chan struct{})
|
||||||
|
go newNode(ready)
|
||||||
|
<-ready
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
// Utilities for testing the websocket service
|
||||||
|
|
||||||
|
// create a new connection
|
||||||
|
func newWSCon(t *testing.T) *websocket.Conn {
|
||||||
|
dialer := websocket.DefaultDialer
|
||||||
|
rHeader := http.Header{}
|
||||||
|
con, r, err := dialer.Dial(websocketAddr, rHeader)
|
||||||
|
fmt.Println("response", r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return con
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe to an event
|
||||||
|
func subscribe(t *testing.T, con *websocket.Conn, eventid string) {
|
||||||
|
err := con.WriteJSON(rpctypes.RPCRequest{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
ID: "",
|
||||||
|
Method: "subscribe",
|
||||||
|
Params: []interface{}{eventid},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsubscribe from an event
|
||||||
|
func unsubscribe(t *testing.T, con *websocket.Conn, eventid string) {
|
||||||
|
err := con.WriteJSON(rpctypes.RPCRequest{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
ID: "",
|
||||||
|
Method: "unsubscribe",
|
||||||
|
Params: []interface{}{eventid},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for an event; do things that might trigger events, and check them when they are received
|
||||||
|
// the check function takes an event id and the byte slice read off the ws
|
||||||
|
func waitForEvent(t *testing.T, con *websocket.Conn, eventid string, dieOnTimeout bool, f func(), check func(string, []byte) error) {
|
||||||
|
// go routine to wait for webscoket msg
|
||||||
|
goodCh := make(chan []byte)
|
||||||
|
errCh := make(chan error)
|
||||||
|
quitCh := make(chan struct{})
|
||||||
|
defer close(quitCh)
|
||||||
|
|
||||||
|
// Read message
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, p, err := con.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
// if the event id isnt what we're waiting on
|
||||||
|
// ignore it
|
||||||
|
var response rpctypes.RPCResponse
|
||||||
|
var err error
|
||||||
|
wire.ReadJSON(&response, p, &err)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
event, ok := response.Result.(*ctypes.ResultEvent)
|
||||||
|
if ok && event.Event == eventid {
|
||||||
|
goodCh <- p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// do stuff (transactions)
|
||||||
|
f()
|
||||||
|
|
||||||
|
// wait for an event or timeout
|
||||||
|
timeout := time.NewTimer(10 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
if dieOnTimeout {
|
||||||
|
con.Close()
|
||||||
|
t.Fatalf("%s event was not received in time", eventid)
|
||||||
|
}
|
||||||
|
// else that's great, we didn't hear the event
|
||||||
|
// and we shouldn't have
|
||||||
|
case p := <-goodCh:
|
||||||
|
if dieOnTimeout {
|
||||||
|
// message was received and expected
|
||||||
|
// run the check
|
||||||
|
err := check(eventid, p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
panic(err) // Show the stack trace.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.Close()
|
||||||
|
t.Fatalf("%s event was not expected", eventid)
|
||||||
|
}
|
||||||
|
case err := <-errCh:
|
||||||
|
t.Fatal(err)
|
||||||
|
panic(err) // Show the stack trace.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func unmarshalResponseNewBlock(b []byte) (*types.Block, error) {
|
||||||
|
// unmarshall and assert somethings
|
||||||
|
var response rpctypes.RPCResponse
|
||||||
|
var err error
|
||||||
|
wire.ReadJSON(&response, b, &err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if response.Error != "" {
|
||||||
|
return nil, fmt.Errorf(response.Error)
|
||||||
|
}
|
||||||
|
block := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataNewBlock).Block
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalValidateBlockchain(t *testing.T, con *websocket.Conn, eid string) {
|
||||||
|
var initBlockN int
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error {
|
||||||
|
block, err := unmarshalResponseNewBlock(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
initBlockN = block.Header.Height
|
||||||
|
} else {
|
||||||
|
if block.Header.Height != initBlockN+i {
|
||||||
|
return fmt.Errorf("Expected block %d, got block %d", i, block.Header.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue