premerge2: rpc -> rpc/tendermint

This commit is contained in:
Ethan Buchman
2017-04-21 17:39:51 -04:00
parent 0017fb7ffe
commit 992b11c450
37 changed files with 0 additions and 0 deletions

View File

@ -1,404 +0,0 @@
package rpctest
import (
"bytes"
crand "crypto/rand"
"fmt"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
. "github.com/tendermint/go-common"
rpc "github.com/tendermint/go-rpc/client"
"github.com/tendermint/tendermint/rpc/core"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state/txindex/null"
"github.com/tendermint/tendermint/types"
)
//--------------------------------------------------------------------------------
// Test the HTTP client
// These tests assume the dummy app
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
// status
func TestURIStatus(t *testing.T) {
testStatus(t, GetURIClient())
}
func TestJSONStatus(t *testing.T) {
testStatus(t, GetJSONClient())
}
func testStatus(t *testing.T, client rpc.HTTPClient) {
chainID := GetConfig().GetString("chain_id")
tmResult := new(ctypes.TMResult)
_, err := client.Call("status", map[string]interface{}{}, tmResult)
require.Nil(t, err)
status := (*tmResult).(*ctypes.ResultStatus)
assert.Equal(t, chainID, status.NodeInfo.Network)
}
//--------------------------------------------------------------------------------
// broadcast tx sync
// random bytes (excluding byte('='))
func randBytes(t *testing.T) []byte {
n := rand.Intn(10) + 2
buf := make([]byte, n)
_, err := crand.Read(buf)
require.Nil(t, err)
return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}
func TestURIBroadcastTxSync(t *testing.T) {
testBroadcastTxSync(t, GetURIClient())
}
func TestJSONBroadcastTxSync(t *testing.T) {
testBroadcastTxSync(t, GetJSONClient())
}
func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) {
config.Set("block_size", 0)
defer config.Set("block_size", -1)
tmResult := new(ctypes.TMResult)
tx := randBytes(t)
_, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult)
require.Nil(t, err)
res := (*tmResult).(*ctypes.ResultBroadcastTx)
require.Equal(t, abci.CodeType_OK, res.Code)
mem := node.MempoolReactor().Mempool
require.Equal(t, 1, mem.Size())
txs := mem.Reap(1)
require.EqualValues(t, tx, txs[0])
mem.Flush()
}
//--------------------------------------------------------------------------------
// query
func testTxKV(t *testing.T) ([]byte, []byte, types.Tx) {
k := randBytes(t)
v := randBytes(t)
return k, v, types.Tx(Fmt("%s=%s", k, v))
}
func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) {
tmResult := new(ctypes.TMResult)
k, v, tx := testTxKV(t)
txBytes := []byte(tx) // XXX
_, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
require.Nil(t, err)
return k, v
}
func TestURIABCIQuery(t *testing.T) {
testABCIQuery(t, GetURIClient())
}
func TestJSONABCIQuery(t *testing.T) {
testABCIQuery(t, GetURIClient())
}
func testABCIQuery(t *testing.T, client rpc.HTTPClient) {
k, _ := sendTx(t, client)
time.Sleep(time.Millisecond * 500)
tmResult := new(ctypes.TMResult)
_, err := client.Call("abci_query",
map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult)
require.Nil(t, err)
resQuery := (*tmResult).(*ctypes.ResultABCIQuery)
require.EqualValues(t, 0, resQuery.Response.Code)
// XXX: specific to value returned by the dummy
require.NotEqual(t, 0, len(resQuery.Response.Value))
}
//--------------------------------------------------------------------------------
// broadcast tx commit
func TestURIBroadcastTxCommit(t *testing.T) {
testBroadcastTxCommit(t, GetURIClient())
}
func TestJSONBroadcastTxCommit(t *testing.T) {
testBroadcastTxCommit(t, GetJSONClient())
}
func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) {
require := require.New(t)
tmResult := new(ctypes.TMResult)
tx := randBytes(t)
_, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult)
require.Nil(err)
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
checkTx := res.CheckTx
require.Equal(abci.CodeType_OK, checkTx.Code)
deliverTx := res.DeliverTx
require.Equal(abci.CodeType_OK, deliverTx.Code)
mem := node.MempoolReactor().Mempool
require.Equal(0, mem.Size())
// TODO: find tx in block
}
//--------------------------------------------------------------------------------
// query tx
func TestURITx(t *testing.T) {
testTx(t, GetURIClient(), true)
core.SetTxIndexer(&null.TxIndex{})
testTx(t, GetJSONClient(), false)
core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
}
func TestJSONTx(t *testing.T) {
testTx(t, GetJSONClient(), true)
core.SetTxIndexer(&null.TxIndex{})
testTx(t, GetJSONClient(), false)
core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
}
func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) {
assert, require := assert.New(t), require.New(t)
// first we broadcast a tx
tmResult := new(ctypes.TMResult)
txBytes := randBytes(t)
tx := types.Tx(txBytes)
_, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
require.Nil(err)
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
checkTx := res.CheckTx
require.Equal(abci.CodeType_OK, checkTx.Code)
deliverTx := res.DeliverTx
require.Equal(abci.CodeType_OK, deliverTx.Code)
mem := node.MempoolReactor().Mempool
require.Equal(0, mem.Size())
txHash := tx.Hash()
txHash2 := types.Tx("a different tx").Hash()
cases := []struct {
valid bool
hash []byte
prove bool
}{
// only valid if correct hash provided
{true, txHash, false},
{true, txHash, true},
{false, txHash2, false},
{false, txHash2, true},
{false, nil, false},
{false, nil, true},
}
for i, tc := range cases {
idx := fmt.Sprintf("%d", i)
// now we query for the tx.
// since there's only one tx, we know index=0.
tmResult = new(ctypes.TMResult)
query := map[string]interface{}{
"hash": tc.hash,
"prove": tc.prove,
}
_, err = client.Call("tx", query, tmResult)
valid := (withIndexer && tc.valid)
if !valid {
require.NotNil(err, idx)
} else {
require.Nil(err, idx)
res2 := (*tmResult).(*ctypes.ResultTx)
assert.Equal(tx, res2.Tx, idx)
assert.Equal(res.Height, res2.Height, idx)
assert.Equal(0, res2.Index, idx)
assert.Equal(abci.CodeType_OK, res2.TxResult.Code, idx)
// time to verify the proof
proof := res2.Proof
if tc.prove && assert.Equal(tx, proof.Data, idx) {
assert.True(proof.Proof.Verify(proof.Index, proof.Total, tx.Hash(), proof.RootHash), idx)
}
}
}
}
//--------------------------------------------------------------------------------
// Test the websocket service
var wsTyp = "JSONRPC"
// make a simple connection to the server
func TestWSConnect(t *testing.T) {
wsc := GetWSClient()
wsc.Stop()
}
// receive a new block message
func TestWSNewBlock(t *testing.T) {
wsc := GetWSClient()
eid := types.EventStringNewBlock()
require.Nil(t, wsc.Subscribe(eid))
defer func() {
require.Nil(t, wsc.Unsubscribe(eid))
wsc.Stop()
}()
waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error {
// fmt.Println("Check:", 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.")
}
wsc := GetWSClient()
eid := types.EventStringNewBlock()
require.Nil(t, wsc.Subscribe(eid))
defer func() {
require.Nil(t, wsc.Unsubscribe(eid))
wsc.Stop()
}()
// listen for NewBlock, ensure height increases by 1
var initBlockN int
for i := 0; i < 3; i++ {
waitForEvent(t, wsc, eid, true, func() {}, func(eid string, eventData interface{}) error {
block := eventData.(types.EventDataNewBlock).Block
if i == 0 {
initBlockN = block.Header.Height
} else {
if block.Header.Height != initBlockN+i {
return fmt.Errorf("Expected block %d, got block %d", initBlockN+i, block.Header.Height)
}
}
return nil
})
}
}
func TestWSTxEvent(t *testing.T) {
require := require.New(t)
wsc := GetWSClient()
tx := randBytes(t)
// listen for the tx I am about to submit
eid := types.EventStringTx(types.Tx(tx))
require.Nil(wsc.Subscribe(eid))
defer func() {
require.Nil(wsc.Unsubscribe(eid))
wsc.Stop()
}()
// send an tx
tmResult := new(ctypes.TMResult)
_, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult)
require.Nil(err)
waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error {
evt, ok := b.(types.EventDataTx)
require.True(ok, "Got wrong event type: %#v", b)
require.Equal(tx, []byte(evt.Tx), "Returned different tx")
require.Equal(abci.CodeType_OK, evt.Code)
return nil
})
}
/* 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
})
}*/
//--------------------------------------------------------------------------------
// unsafe_set_config
var stringVal = "my string"
var intVal = 987654321
var boolVal = true
// don't change these
var testCasesUnsafeSetConfig = [][]string{
[]string{"string", "key1", stringVal},
[]string{"int", "key2", fmt.Sprintf("%v", intVal)},
[]string{"bool", "key3", fmt.Sprintf("%v", boolVal)},
}
func TestURIUnsafeSetConfig(t *testing.T) {
for _, testCase := range testCasesUnsafeSetConfig {
tmResult := new(ctypes.TMResult)
_, err := GetURIClient().Call("unsafe_set_config", map[string]interface{}{
"type": testCase[0],
"key": testCase[1],
"value": testCase[2],
}, tmResult)
require.Nil(t, err)
}
testUnsafeSetConfig(t)
}
func TestJSONUnsafeSetConfig(t *testing.T) {
for _, testCase := range testCasesUnsafeSetConfig {
tmResult := new(ctypes.TMResult)
_, err := GetJSONClient().Call("unsafe_set_config",
map[string]interface{}{"type": testCase[0], "key": testCase[1], "value": testCase[2]},
tmResult)
require.Nil(t, err)
}
testUnsafeSetConfig(t)
}
func testUnsafeSetConfig(t *testing.T) {
require := require.New(t)
s := config.GetString("key1")
require.Equal(stringVal, s)
i := config.GetInt("key2")
require.Equal(intVal, i)
b := config.GetBool("key3")
require.Equal(boolVal, b)
}

View File

@ -1,20 +0,0 @@
package rpctest
import (
"testing"
"golang.org/x/net/context"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/rpc/grpc"
)
//-------------------------------------------
func TestBroadcastTx(t *testing.T) {
require := require.New(t)
res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")})
require.Nil(err)
require.EqualValues(0, res.CheckTx.Code)
require.EqualValues(0, res.DeliverTx.Code)
}

View File

@ -1,183 +0,0 @@
package rpctest
import (
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
logger "github.com/tendermint/go-logger"
wire "github.com/tendermint/go-wire"
abci "github.com/tendermint/abci/types"
cfg "github.com/tendermint/go-config"
client "github.com/tendermint/go-rpc/client"
"github.com/tendermint/tendermint/config/tendermint_test"
nm "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/proxy"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
core_grpc "github.com/tendermint/tendermint/rpc/grpc"
"github.com/tendermint/tendermint/types"
)
var (
config cfg.Config
)
const tmLogLevel = "error"
// f**ing long, but unique for each test
func makePathname() string {
// get path
p, err := os.Getwd()
if err != nil {
panic(err)
}
fmt.Println(p)
sep := string(filepath.Separator)
return strings.Replace(p, sep, "_", -1)
}
func randPort() int {
// returns between base and base + spread
base, spread := 20000, 20000
return base + rand.Intn(spread)
}
func makeAddrs() (string, string, string) {
start := randPort()
return fmt.Sprintf("tcp://0.0.0.0:%d", start),
fmt.Sprintf("tcp://0.0.0.0:%d", start+1),
fmt.Sprintf("tcp://0.0.0.0:%d", start+2)
}
// GetConfig returns a config for the test cases as a singleton
func GetConfig() cfg.Config {
if config == nil {
pathname := makePathname()
config = tendermint_test.ResetConfig(pathname)
// Shut up the logging
logger.SetLogLevel(tmLogLevel)
// and we use random ports to run in parallel
tm, rpc, grpc := makeAddrs()
config.Set("node_laddr", tm)
config.Set("rpc_laddr", rpc)
config.Set("grpc_laddr", grpc)
}
return config
}
// GetURIClient gets a uri client pointing to the test tendermint rpc
func GetURIClient() *client.URIClient {
rpcAddr := GetConfig().GetString("rpc_laddr")
return client.NewURIClient(rpcAddr)
}
// GetJSONClient gets a http/json client pointing to the test tendermint rpc
func GetJSONClient() *client.JSONRPCClient {
rpcAddr := GetConfig().GetString("rpc_laddr")
return client.NewJSONRPCClient(rpcAddr)
}
func GetGRPCClient() core_grpc.BroadcastAPIClient {
grpcAddr := config.GetString("grpc_laddr")
return core_grpc.StartGRPCClient(grpcAddr)
}
func GetWSClient() *client.WSClient {
rpcAddr := GetConfig().GetString("rpc_laddr")
wsc := client.NewWSClient(rpcAddr, "/websocket")
if _, err := wsc.Start(); err != nil {
panic(err)
}
return wsc
}
// StartTendermint starts a test tendermint server in a go routine and returns when it is initialized
func StartTendermint(app abci.Application) *nm.Node {
node := NewTendermint(app)
node.Start()
fmt.Println("Tendermint running!")
return node
}
// NewTendermint creates a new tendermint server and sleeps forever
func NewTendermint(app abci.Application) *nm.Node {
// Create & start node
config := GetConfig()
privValidatorFile := config.GetString("priv_validator_file")
privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
papp := proxy.NewLocalClientCreator(app)
node := nm.NewNode(config, privValidator, papp)
return node
}
//--------------------------------------------------------------------------------
// Utilities for testing the websocket service
// 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, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) {
// go routine to wait for webscoket msg
goodCh := make(chan interface{})
errCh := make(chan error)
// Read message
go func() {
var err error
LOOP:
for {
select {
case r := <-wsc.ResultsCh:
result := new(ctypes.TMResult)
wire.ReadJSONPtr(result, r, &err)
if err != nil {
errCh <- err
break LOOP
}
event, ok := (*result).(*ctypes.ResultEvent)
if ok && event.Name == eventid {
goodCh <- event.Data
break LOOP
}
case err := <-wsc.ErrorsCh:
errCh <- err
break LOOP
case <-wsc.Quit:
break LOOP
}
}
}()
// do stuff (transactions)
f()
// wait for an event or timeout
timeout := time.NewTimer(10 * time.Second)
select {
case <-timeout.C:
if dieOnTimeout {
wsc.Stop()
require.True(t, false, "%s event was not received in time", eventid)
}
// else that's great, we didn't hear the event
// and we shouldn't have
case eventData := <-goodCh:
if dieOnTimeout {
// message was received and expected
// run the check
require.Nil(t, check(eventid, eventData))
} else {
wsc.Stop()
require.True(t, false, "%s event was not expected", eventid)
}
case err := <-errCh:
panic(err) // Show the stack trace.
}
}
//--------------------------------------------------------------------------------

View File

@ -1,36 +0,0 @@
/*
package tests contain integration tests and helper functions for testing
the RPC interface
In particular, it allows us to spin up a tendermint node in process, with
a live RPC server, which we can use to verify our rpc calls. It provides
all data structures, enabling us to do more complex tests (like node_test.go)
that introspect the blocks themselves to validate signatures and the like.
It currently only spins up one node, it would be interesting to expand it
to multiple nodes to see the real effects of validating partially signed
blocks.
*/
package rpctest
import (
"os"
"testing"
"github.com/tendermint/abci/example/dummy"
nm "github.com/tendermint/tendermint/node"
)
var node *nm.Node
func TestMain(m *testing.M) {
// start a tendermint node (and merkleeyes) in the background to test against
app := dummy.NewDummyApplication()
node = StartTendermint(app)
code := m.Run()
// and shut down proper at the end
node.Stop()
node.Wait()
os.Exit(code)
}