Merge PR #1191: LCD cleanup / add LCD gas field
* remove global variables from lcd * added make race, fix lcd race condition * cleanup * Five-character changelog update
This commit is contained in:
parent
8ece807301
commit
1b20adcd22
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,6 +14,7 @@ docs/_build
|
||||
# Data - ideally these don't exist
|
||||
examples/basecoin/app/data
|
||||
baseapp/data/*
|
||||
client/lcd/keys/*
|
||||
|
||||
# Testing
|
||||
coverage.txt
|
||||
@ -26,4 +27,4 @@ profile.out
|
||||
vagrant
|
||||
|
||||
# Graphviz
|
||||
dependency-graph.png
|
||||
dependency-graph.png
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
BREAKING CHANGES
|
||||
* msg.GetSignBytes() now returns bech32-encoded addresses in all cases
|
||||
* [lcd] REST end-points now include gas
|
||||
|
||||
FEATURES
|
||||
|
||||
@ -9,10 +10,11 @@ IMPROVEMENTS
|
||||
* export command now writes current validator set for Tendermint
|
||||
* [tests] Application module tests now use a mock application
|
||||
* [gaiacli] Fix error message when account isn't found when running gaiacli account
|
||||
* [lcd] refactored to eliminate use of global variables, and interdependent tests
|
||||
|
||||
FIXES
|
||||
* [lcd] Switch to bech32 for addresses on all human readable inputs and outputs
|
||||
* fixed tx indexing/querying
|
||||
* [lcd] fixed tx indexing/querying
|
||||
* [cli] Added `--gas` flag to specify transaction gas limit
|
||||
|
||||
## 0.18.0
|
||||
|
||||
3
Makefile
3
Makefile
@ -92,6 +92,9 @@ test_cli:
|
||||
test_unit:
|
||||
@go test $(PACKAGES_NOCLITEST)
|
||||
|
||||
test_race:
|
||||
@go test -race $(PACKAGES_NOCLITEST)
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ const KeyDBName = "keys"
|
||||
// keybase is used to make GetKeyBase a singleton
|
||||
var keybase keys.Keybase
|
||||
|
||||
// TODO make keybase take a database not load from the directory
|
||||
|
||||
// initialize a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
package lcd
|
||||
|
||||
// NOTE: COPIED VERBATIM FROM tendermint/tendermint/rpc/test/helpers.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
)
|
||||
|
||||
var globalConfig *cfg.Config
|
||||
|
||||
// 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 {
|
||||
return int(cmn.RandUint16()/2 + 10000)
|
||||
}
|
||||
|
||||
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 globalConfig == nil {
|
||||
pathname := makePathname()
|
||||
globalConfig = cfg.ResetTestRoot(pathname)
|
||||
|
||||
// and we use random ports to run in parallel
|
||||
tm, rpc, _ := makeAddrs()
|
||||
globalConfig.P2P.ListenAddress = tm
|
||||
globalConfig.RPC.ListenAddress = rpc
|
||||
}
|
||||
return globalConfig
|
||||
}
|
||||
@ -1,146 +1,115 @@
|
||||
package lcd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
cryptoKeys "github.com/tendermint/go-crypto/keys"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
p2p "github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
tests "github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
stakerest "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
)
|
||||
|
||||
var (
|
||||
coinDenom = "steak"
|
||||
coinAmount = int64(10000000)
|
||||
|
||||
validatorAddr1Hx = ""
|
||||
validatorAddr2Hx = ""
|
||||
validatorAddr1 = ""
|
||||
validatorAddr2 = ""
|
||||
|
||||
// XXX bad globals
|
||||
name = "test"
|
||||
password = "0123456789"
|
||||
port string
|
||||
seed string
|
||||
sendAddr string
|
||||
)
|
||||
|
||||
func TestKeys(t *testing.T) {
|
||||
|
||||
// empty keys
|
||||
// XXX: the test comes with a key setup
|
||||
/*
|
||||
res, body := request(t, port, "GET", "/keys", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
assert.Equal(t, "[]", body, "Expected an empty array")
|
||||
*/
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// get seed
|
||||
res, body := request(t, port, "GET", "/keys/seed", nil)
|
||||
res, body := Request(t, port, "GET", "/keys/seed", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
newSeed := body
|
||||
reg, err := regexp.Compile(`([a-z]+ ){12}`)
|
||||
require.Nil(t, err)
|
||||
match := reg.MatchString(seed)
|
||||
assert.True(t, match, "Returned seed has wrong foramt", seed)
|
||||
assert.True(t, match, "Returned seed has wrong format", seed)
|
||||
|
||||
newName := "test_newname"
|
||||
newPassword := "0987654321"
|
||||
|
||||
// add key
|
||||
var jsonStr = []byte(fmt.Sprintf(`{"name":"test_fail", "password":"%s"}`, password))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode, "Account creation should require a seed")
|
||||
|
||||
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
addr := body
|
||||
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
|
||||
addr2 := body
|
||||
assert.Len(t, addr2, 40, "Returned address has wrong format", addr2)
|
||||
|
||||
// existing keys
|
||||
res, body = request(t, port, "GET", "/keys", nil)
|
||||
res, body = Request(t, port, "GET", "/keys", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var m [2]keys.KeyOutput
|
||||
err = cdc.UnmarshalJSON([]byte(body), &m)
|
||||
require.Nil(t, err)
|
||||
|
||||
addrAcc, _ := sdk.GetAccAddressHex(addr)
|
||||
addrBech32, _ := sdk.Bech32ifyAcc(addrAcc)
|
||||
addr2Acc, err := sdk.GetAccAddressHex(addr2)
|
||||
require.Nil(t, err)
|
||||
addr2Bech32 := sdk.MustBech32ifyAcc(addr2Acc)
|
||||
addrBech32 := sdk.MustBech32ifyAcc(addr)
|
||||
|
||||
assert.Equal(t, name, m[0].Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, sendAddr, m[0].Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, addrBech32, m[0].Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, newName, m[1].Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, addrBech32, m[1].Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, addr2Bech32, m[1].Address, "Did not serve keys Address correctly")
|
||||
|
||||
// select key
|
||||
keyEndpoint := fmt.Sprintf("/keys/%s", newName)
|
||||
res, body = request(t, port, "GET", keyEndpoint, nil)
|
||||
res, body = Request(t, port, "GET", keyEndpoint, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var m2 keys.KeyOutput
|
||||
err = cdc.UnmarshalJSON([]byte(body), &m2)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Equal(t, newName, m2.Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, addrBech32, m2.Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, addr2Bech32, m2.Address, "Did not serve keys Address correctly")
|
||||
|
||||
// update key
|
||||
jsonStr = []byte(fmt.Sprintf(`{"old_password":"%s", "new_password":"12345678901"}`, newPassword))
|
||||
res, body = request(t, port, "PUT", keyEndpoint, jsonStr)
|
||||
jsonStr = []byte(fmt.Sprintf(`{
|
||||
"old_password":"%s",
|
||||
"new_password":"12345678901"
|
||||
}`, newPassword))
|
||||
|
||||
res, body = Request(t, port, "PUT", keyEndpoint, jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
// here it should say unauthorized as we changed the password before
|
||||
res, body = request(t, port, "PUT", keyEndpoint, jsonStr)
|
||||
res, body = Request(t, port, "PUT", keyEndpoint, jsonStr)
|
||||
require.Equal(t, http.StatusUnauthorized, res.StatusCode, body)
|
||||
|
||||
// delete key
|
||||
jsonStr = []byte(`{"password":"12345678901"}`)
|
||||
res, body = request(t, port, "DELETE", keyEndpoint, jsonStr)
|
||||
res, body = Request(t, port, "DELETE", keyEndpoint, jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
// node info
|
||||
res, body := request(t, port, "GET", "/version", nil)
|
||||
res, body := Request(t, port, "GET", "/version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
@ -150,9 +119,11 @@ func TestVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNodeStatus(t *testing.T) {
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
// node info
|
||||
res, body := request(t, port, "GET", "/node_info", nil)
|
||||
res, body := Request(t, port, "GET", "/node_info", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var nodeInfo p2p.NodeInfo
|
||||
@ -162,21 +133,20 @@ func TestNodeStatus(t *testing.T) {
|
||||
assert.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res)
|
||||
|
||||
// syncing
|
||||
res, body = request(t, port, "GET", "/syncing", nil)
|
||||
res, body = Request(t, port, "GET", "/syncing", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
// we expect that there is no other node running so the syncing state is "false"
|
||||
// we c
|
||||
assert.Equal(t, "false", body)
|
||||
}
|
||||
|
||||
func TestBlock(t *testing.T) {
|
||||
|
||||
tests.WaitForHeight(2, port)
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
var resultBlock ctypes.ResultBlock
|
||||
|
||||
res, body := request(t, port, "GET", "/blocks/latest", nil)
|
||||
res, body := Request(t, port, "GET", "/blocks/latest", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
@ -186,7 +156,7 @@ func TestBlock(t *testing.T) {
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/blocks/1", nil)
|
||||
res, body = Request(t, port, "GET", "/blocks/1", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = json.Unmarshal([]byte(body), &resultBlock)
|
||||
@ -196,15 +166,17 @@ func TestBlock(t *testing.T) {
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/blocks/1000000000", nil)
|
||||
res, body = Request(t, port, "GET", "/blocks/1000000000", nil)
|
||||
require.Equal(t, http.StatusNotFound, res.StatusCode, body)
|
||||
}
|
||||
|
||||
func TestValidators(t *testing.T) {
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
var resultVals rpc.ResultValidatorsOutput
|
||||
|
||||
res, body := request(t, port, "GET", "/validatorsets/latest", nil)
|
||||
res, body := Request(t, port, "GET", "/validatorsets/latest", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultVals)
|
||||
@ -217,7 +189,7 @@ func TestValidators(t *testing.T) {
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/validatorsets/1", nil)
|
||||
res, body = Request(t, port, "GET", "/validatorsets/1", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultVals)
|
||||
@ -227,23 +199,29 @@ func TestValidators(t *testing.T) {
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/validatorsets/1000000000", nil)
|
||||
res, body = Request(t, port, "GET", "/validatorsets/1000000000", nil)
|
||||
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
}
|
||||
|
||||
func TestCoinSend(t *testing.T) {
|
||||
bz, _ := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
||||
someFakeAddr, _ := sdk.Bech32ifyAcc(bz)
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
||||
require.NoError(t, err)
|
||||
someFakeAddr := sdk.MustBech32ifyAcc(bz)
|
||||
|
||||
// query empty
|
||||
res, body := request(t, port, "GET", "/accounts/"+someFakeAddr, nil)
|
||||
res, body := Request(t, port, "GET", "/accounts/"+someFakeAddr, nil)
|
||||
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
initialBalance := acc.GetCoins()
|
||||
|
||||
// create TX
|
||||
receiveAddr, resultTx := doSend(t, port)
|
||||
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
@ -251,27 +229,31 @@ func TestCoinSend(t *testing.T) {
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc = getAccount(t, sendAddr)
|
||||
acc = getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
mycoins := coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, initialBalance[0].Amount-1, mycoins.Amount)
|
||||
|
||||
// query receiver
|
||||
acc = getAccount(t, receiveAddr)
|
||||
acc = getAccount(t, port, receiveAddr)
|
||||
coins = acc.GetCoins()
|
||||
mycoins = coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, int64(1), mycoins.Amount)
|
||||
}
|
||||
|
||||
func TestIBCTransfer(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
initialBalance := acc.GetCoins()
|
||||
|
||||
// create TX
|
||||
resultTx := doIBCTransfer(t, port, seed)
|
||||
resultTx := doIBCTransfer(t, port, seed, name, password, addr)
|
||||
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
@ -280,32 +262,37 @@ func TestIBCTransfer(t *testing.T) {
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc = getAccount(t, sendAddr)
|
||||
acc = getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
mycoins := coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, initialBalance[0].Amount-1, mycoins.Amount)
|
||||
|
||||
// TODO: query ibc egress packet state
|
||||
}
|
||||
|
||||
func TestTxs(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// query wrong
|
||||
res, body := request(t, port, "GET", "/txs", nil)
|
||||
res, body := Request(t, port, "GET", "/txs", nil)
|
||||
require.Equal(t, http.StatusBadRequest, res.StatusCode, body)
|
||||
|
||||
// query empty
|
||||
res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "cosmosaccaddr1jawd35d9aq4u76sr3fjalmcqc8hqygs9gtnmv3"), nil)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "cosmosaccaddr1jawd35d9aq4u76sr3fjalmcqc8hqygs9gtnmv3"), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
assert.Equal(t, "[]", body)
|
||||
|
||||
// create TX
|
||||
receiveAddr, resultTx := doSend(t, port)
|
||||
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
|
||||
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx is findable
|
||||
res, body = request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
type txInfo struct {
|
||||
@ -316,53 +303,67 @@ func TestTxs(t *testing.T) {
|
||||
var indexedTxs []txInfo
|
||||
|
||||
// check if tx is queryable
|
||||
res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
assert.NotEqual(t, "[]", body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &indexedTxs)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(indexedTxs), 1)
|
||||
assert.Equal(t, 1, len(indexedTxs))
|
||||
|
||||
// query sender
|
||||
res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", sendAddr), nil)
|
||||
addrBech := sdk.MustBech32ifyAcc(addr)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", addrBech), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(indexedTxs)) // there are 2 txs created with doSend
|
||||
assert.Equal(t, resultTx.Height, indexedTxs[1].Height)
|
||||
require.Equal(t, 1, len(indexedTxs), "%v", indexedTxs) // there are 2 txs created with doSend
|
||||
assert.Equal(t, resultTx.Height, indexedTxs[0].Height)
|
||||
|
||||
// query recipient
|
||||
res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddr), nil)
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddrBech), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(indexedTxs))
|
||||
require.Equal(t, 1, len(indexedTxs))
|
||||
assert.Equal(t, resultTx.Height, indexedTxs[0].Height)
|
||||
}
|
||||
|
||||
func TestValidatorsQuery(t *testing.T) {
|
||||
validators := getValidators(t)
|
||||
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{})
|
||||
require.Equal(t, 2, len(pks))
|
||||
defer cleanup()
|
||||
|
||||
validators := getValidators(t, port)
|
||||
assert.Equal(t, len(validators), 2)
|
||||
|
||||
// make sure all the validators were found (order unknown because sorted by owner addr)
|
||||
foundVal1, foundVal2 := false, false
|
||||
if validators[0].Owner == validatorAddr1 || validators[1].Owner == validatorAddr1 {
|
||||
pk1Bech := sdk.MustBech32ifyValPub(pks[0])
|
||||
pk2Bech := sdk.MustBech32ifyValPub(pks[1])
|
||||
if validators[0].PubKey == pk1Bech || validators[1].PubKey == pk1Bech {
|
||||
foundVal1 = true
|
||||
}
|
||||
if validators[0].Owner == validatorAddr2 || validators[1].Owner == validatorAddr2 {
|
||||
if validators[0].PubKey == pk2Bech || validators[1].PubKey == pk2Bech {
|
||||
foundVal2 = true
|
||||
}
|
||||
assert.True(t, foundVal1, "validatorAddr1 %v, owner1 %v, owner2 %v", validatorAddr1, validators[0].Owner, validators[1].Owner)
|
||||
assert.True(t, foundVal2, "validatorAddr2 %v, owner1 %v, owner2 %v", validatorAddr2, validators[0].Owner, validators[1].Owner)
|
||||
assert.True(t, foundVal1, "pk1Bech %v, owner1 %v, owner2 %v", pk1Bech, validators[0].Owner, validators[1].Owner)
|
||||
assert.True(t, foundVal2, "pk2Bech %v, owner1 %v, owner2 %v", pk2Bech, validators[0].Owner, validators[1].Owner)
|
||||
}
|
||||
|
||||
func TestBond(t *testing.T) {
|
||||
func TestBonding(t *testing.T) {
|
||||
name, password, denom := "test", "1234567890", "steak"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
validator1Owner := pks[0].Address()
|
||||
|
||||
// create bond TX
|
||||
resultTx := doBond(t, port, seed)
|
||||
resultTx := doBond(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
@ -370,201 +371,41 @@ func TestBond(t *testing.T) {
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
assert.Equal(t, int64(87), coins.AmountOf(coinDenom))
|
||||
assert.Equal(t, int64(40), coins.AmountOf(denom))
|
||||
|
||||
// query candidate
|
||||
bond := getDelegation(t, sendAddr, validatorAddr1)
|
||||
assert.Equal(t, "10/1", bond.Shares.String())
|
||||
}
|
||||
// query validator
|
||||
bond := getDelegation(t, port, addr, validator1Owner)
|
||||
assert.Equal(t, "60/1", bond.Shares.String())
|
||||
|
||||
func TestUnbond(t *testing.T) {
|
||||
//////////////////////
|
||||
// testing unbonding
|
||||
|
||||
// create unbond TX
|
||||
resultTx := doUnbond(t, port, seed)
|
||||
resultTx = doUnbond(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// query validator
|
||||
bond = getDelegation(t, port, addr, validator1Owner)
|
||||
assert.Equal(t, "30/1", bond.Shares.String())
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// TODO fix shares fn in staking
|
||||
// query sender
|
||||
acc := getAccount(t, sendAddr)
|
||||
coins := acc.GetCoins()
|
||||
assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
|
||||
|
||||
// query candidate
|
||||
bond := getDelegation(t, sendAddr, validatorAddr1)
|
||||
assert.Equal(t, "9/1", bond.Shares.String())
|
||||
//acc = getAccount(t, port, addr)
|
||||
//coins = acc.GetCoins()
|
||||
//assert.Equal(t, int64(70), coins.AmountOf(denom))
|
||||
}
|
||||
|
||||
//__________________________________________________________
|
||||
// helpers
|
||||
|
||||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
||||
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
viper.Set(client.FlagGas, 200000)
|
||||
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
config := GetConfig()
|
||||
config.Consensus.TimeoutCommit = 1000
|
||||
config.Consensus.SkipTimeoutCommit = false
|
||||
config.TxIndex.IndexAllTags = true
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
genDoc.Validators = append(genDoc.Validators,
|
||||
tmtypes.GenesisValidator{
|
||||
PubKey: crypto.GenPrivKeyEd25519().PubKey(),
|
||||
Power: 1,
|
||||
Name: "val",
|
||||
},
|
||||
)
|
||||
|
||||
pk1 := genDoc.Validators[0].PubKey
|
||||
pk2 := genDoc.Validators[1].PubKey
|
||||
validatorAddr1Hx = hex.EncodeToString(pk1.Address())
|
||||
validatorAddr2Hx = hex.EncodeToString(pk2.Address())
|
||||
validatorAddr1, _ = sdk.Bech32ifyVal(pk1.Address())
|
||||
validatorAddr2, _ = sdk.Bech32ifyVal(pk2.Address())
|
||||
|
||||
// NOTE it's bad practice to reuse pk address for the owner address but doing in the
|
||||
// test for simplicity
|
||||
var appGenTxs [2]json.RawMessage
|
||||
appGenTxs[0], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk1, pk1.Address(), "test_val1", true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
appGenTxs[1], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk2, pk2.Address(), "test_val2", true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// add the sendAddr to genesis
|
||||
var info cryptoKeys.Info
|
||||
info, seed, err = kb.Create(name, password, cryptoKeys.AlgoEd25519) // XXX global seed
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sendAddrHex, _ := sdk.GetAccAddressHex(info.PubKey.Address().String())
|
||||
sendAddr, _ = sdk.Bech32ifyAcc(sendAddrHex) // XXX global
|
||||
accAuth := auth.NewBaseAccountWithAddress(info.PubKey.Address())
|
||||
accAuth.Coins = sdk.Coins{{"steak", 100}}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
genDoc.AppStateJSON = appState
|
||||
|
||||
// LCD listen address
|
||||
var listenAddr string
|
||||
listenAddr, port, err = server.FreeTCPAddr()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// XXX: need to set this so LCD knows the tendermint node address!
|
||||
viper.Set(client.FlagNode, config.RPC.ListenAddress)
|
||||
viper.Set(client.FlagChainID, genDoc.ChainID)
|
||||
|
||||
node, err := startTM(config, logger, genDoc, privVal, app)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lcd, err := startLCD(logger, listenAddr, cdc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tests.WaitForStart(port)
|
||||
|
||||
return node, lcd, nil
|
||||
}
|
||||
|
||||
// Create & start in-process tendermint node with memdb
|
||||
// and in-process abci application.
|
||||
// TODO: need to clean up the WAL dir or enable it to be not persistent
|
||||
func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, privVal tmtypes.PrivValidator, app abci.Application) (*nm.Node, error) {
|
||||
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil }
|
||||
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil }
|
||||
n, err := nm.NewNode(cfg,
|
||||
privVal,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
dbProvider,
|
||||
logger.With("module", "node"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wait for rpc
|
||||
tests.WaitForRPC(GetConfig().RPC.ListenAddress)
|
||||
|
||||
logger.Info("Tendermint running!")
|
||||
return n, err
|
||||
}
|
||||
|
||||
// start the LCD. note this blocks!
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
|
||||
handler := createHandler(cdc)
|
||||
return tmrpc.StartHTTPServer(listenAddr, handler, logger)
|
||||
}
|
||||
|
||||
func request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, path)
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
|
||||
output, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
return res, string(output)
|
||||
}
|
||||
|
||||
func getAccount(t *testing.T, sendAddr string) auth.Account {
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
|
||||
//_____________________________________________________________________________
|
||||
// get the account to get the sequence
|
||||
func getAccount(t *testing.T, port string, addr sdk.Address) auth.Account {
|
||||
addrBech32 := sdk.MustBech32ifyAcc(addr)
|
||||
res, body := Request(t, port, "GET", "/accounts/"+addrBech32, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var acc auth.Account
|
||||
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
||||
@ -572,20 +413,32 @@ func getAccount(t *testing.T, sendAddr string) auth.Account {
|
||||
return acc
|
||||
}
|
||||
|
||||
func doSend(t *testing.T, port string) (receiveAddr string, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (receiveAddr sdk.Address, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
|
||||
require.Nil(t, err)
|
||||
receiveAddr, _ = sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
|
||||
receiveAddr = receiveInfo.PubKey.Address()
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
|
||||
res, body := request(t, port, "POST", "/accounts/"+receiveAddr+"/send", jsonStr)
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name":"%s",
|
||||
"password":"%s",
|
||||
"sequence":%d,
|
||||
"gas": 10000,
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}`, name, password, sequence, "steak"))
|
||||
res, body := Request(t, port, "POST", "/accounts/"+receiveAddrBech+"/send", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
@ -594,20 +447,32 @@ func doSend(t *testing.T, port string) (receiveAddr string, resultTx ctypes.Resu
|
||||
return receiveAddr, resultTx
|
||||
}
|
||||
|
||||
func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
|
||||
require.Nil(t, err)
|
||||
receiveAddr, _ := sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
|
||||
receiveAddr := receiveInfo.PubKey.Address()
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
|
||||
res, body := request(t, port, "POST", "/ibc/testchain/"+receiveAddr+"/send", jsonStr)
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name":"%s",
|
||||
"password": "%s",
|
||||
"sequence": %d,
|
||||
"gas": 100000,
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}`, name, password, sequence, "steak"))
|
||||
res, body := Request(t, port, "POST", "/ibc/testchain/"+receiveAddrBech+"/send", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
@ -616,9 +481,13 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
||||
return resultTx
|
||||
}
|
||||
|
||||
func getDelegation(t *testing.T, delegatorAddr, candidateAddr string) stake.Delegation {
|
||||
func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.Address) stake.Delegation {
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/stake/"+delegatorAddr+"/bonding_status/"+candidateAddr, nil)
|
||||
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/bonding_status/"+validatorAddrBech, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var bond stake.Delegation
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bond)
|
||||
@ -626,26 +495,30 @@ func getDelegation(t *testing.T, delegatorAddr, candidateAddr string) stake.Dele
|
||||
return bond
|
||||
}
|
||||
|
||||
func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"sequence": %d,
|
||||
"gas": 10000,
|
||||
"delegate": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"bond": { "denom": "%s", "amount": 10 }
|
||||
"bond": { "denom": "%s", "amount": 60 }
|
||||
}
|
||||
],
|
||||
"unbond": []
|
||||
}`, name, password, sequence, sendAddr, validatorAddr1, coinDenom))
|
||||
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
}`, name, password, sequence, delegatorAddrBech, validatorAddrBech, "steak"))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results []ctypes.ResultBroadcastTxCommit
|
||||
@ -655,26 +528,30 @@ func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxC
|
||||
return results[0]
|
||||
}
|
||||
|
||||
func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"sequence": %d,
|
||||
"bond": [],
|
||||
"gas": 10000,
|
||||
"delegate": [],
|
||||
"unbond": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"shares": "1"
|
||||
"shares": "30"
|
||||
}
|
||||
]
|
||||
}`, name, password, sequence, sendAddr, validatorAddr1))
|
||||
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
}`, name, password, sequence, delegatorAddrBech, validatorAddrBech))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results []ctypes.ResultBroadcastTxCommit
|
||||
@ -684,9 +561,9 @@ func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastT
|
||||
return results[0]
|
||||
}
|
||||
|
||||
func getValidators(t *testing.T) []stakerest.StakeValidatorOutput {
|
||||
func getValidators(t *testing.T, port string) []stakerest.StakeValidatorOutput {
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/stake/validators", nil)
|
||||
res, body := Request(t, port, "GET", "/stake/validators", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validators []stakerest.StakeValidatorOutput
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validators)
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
package lcd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
)
|
||||
|
||||
var node *nm.Node
|
||||
|
||||
// See https://golang.org/pkg/testing/#hdr-Main
|
||||
// for more details
|
||||
func TestMain(m *testing.M) {
|
||||
// start a basecoind node and LCD server in the background to test against
|
||||
|
||||
// run all the tests against a single server instance
|
||||
node, lcd, err := startTMAndLCD()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
// tear down
|
||||
// TODO: cleanup
|
||||
// TODO: it would be great if TM could run without
|
||||
// persiting anything in the first place
|
||||
node.Stop()
|
||||
node.Wait()
|
||||
|
||||
// just a listener ...
|
||||
lcd.Close()
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
@ -25,19 +25,34 @@ import (
|
||||
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
flagListenAddr = "laddr"
|
||||
flagCORS = "cors"
|
||||
)
|
||||
|
||||
// ServeCommand will generate a long-running rest server
|
||||
// (aka Light Client Daemon) that exposes functionality similar
|
||||
// to the cli, but over rest
|
||||
func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
||||
flagListenAddr := "laddr"
|
||||
flagCORS := "cors"
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rest-server",
|
||||
Short: "Start LCD (light-client daemon), a local REST server",
|
||||
RunE: startRESTServerFn(cdc),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr := viper.GetString(flagListenAddr)
|
||||
handler := createHandler(cdc)
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||
With("module", "rest-server")
|
||||
listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("REST server started")
|
||||
|
||||
// Wait forever and cleanup
|
||||
cmn.TrapSignal(func() {
|
||||
err := listener.Close()
|
||||
logger.Error("Error closing listener", "err", err)
|
||||
})
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP(flagListenAddr, "a", "tcp://localhost:1317", "Address for server to listen on")
|
||||
cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)")
|
||||
@ -46,27 +61,6 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr := viper.GetString(flagListenAddr)
|
||||
handler := createHandler(cdc)
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||
With("module", "rest-server")
|
||||
listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("REST server started")
|
||||
|
||||
// Wait forever and cleanup
|
||||
cmn.TrapSignal(func() {
|
||||
err := listener.Close()
|
||||
logger.Error("Error closing listener", "err", err)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func createHandler(cdc *wire.Codec) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
|
||||
|
||||
234
client/lcd/test_helpers.go
Normal file
234
client/lcd/test_helpers.go
Normal file
@ -0,0 +1,234 @@
|
||||
package lcd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
crkeys "github.com/tendermint/go-crypto/keys"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
)
|
||||
|
||||
// f**ing long, but unique for each test
|
||||
func makePathname() string {
|
||||
// get path
|
||||
p, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sep := string(filepath.Separator)
|
||||
return strings.Replace(p, sep, "_", -1)
|
||||
}
|
||||
|
||||
// GetConfig returns a config for the test cases as a singleton
|
||||
func GetConfig() *tmcfg.Config {
|
||||
pathname := makePathname()
|
||||
config := tmcfg.ResetTestRoot(pathname)
|
||||
|
||||
tmAddr, _, err := server.FreeTCPAddr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rcpAddr, _, err := server.FreeTCPAddr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
config.P2P.ListenAddress = tmAddr
|
||||
config.RPC.ListenAddress = rcpAddr
|
||||
return config
|
||||
}
|
||||
|
||||
// get the lcd test keybase
|
||||
// note can't use a memdb because the request is expecting to interact with the default location
|
||||
func GetKB(t *testing.T) crkeys.Keybase {
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
require.NoError(t, err)
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
keybase, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
require.NoError(t, err)
|
||||
return keybase
|
||||
}
|
||||
|
||||
// add an address to the store return name and password
|
||||
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (addr sdk.Address, seed string) {
|
||||
var info crkeys.Info
|
||||
var err error
|
||||
info, seed, err = kb.Create(name, password, crkeys.AlgoEd25519)
|
||||
require.NoError(t, err)
|
||||
addr = info.PubKey.Address()
|
||||
return
|
||||
}
|
||||
|
||||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
// nValidators = number of validators
|
||||
// initAddrs = accounts to initialize with some steaks
|
||||
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (cleanup func(), validatorsPKs []crypto.PubKey, port string) {
|
||||
|
||||
config := GetConfig()
|
||||
config.Consensus.TimeoutCommit = 1000
|
||||
config.Consensus.SkipTimeoutCommit = false
|
||||
config.TxIndex.IndexAllTags = true
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
privVal.Reset()
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add more validators
|
||||
if nValidators < 1 {
|
||||
panic("InitializeTestLCD must use at least one validator")
|
||||
}
|
||||
for i := 1; i < nValidators; i++ {
|
||||
genDoc.Validators = append(genDoc.Validators,
|
||||
tmtypes.GenesisValidator{
|
||||
PubKey: crypto.GenPrivKeyEd25519().PubKey(),
|
||||
Power: 1,
|
||||
Name: "val",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// NOTE it's bad practice to reuse pk address for the owner address but doing in the
|
||||
// test for simplicity
|
||||
var appGenTxs []json.RawMessage
|
||||
for _, gdValidator := range genDoc.Validators {
|
||||
pk := gdValidator.PubKey
|
||||
validatorsPKs = append(validatorsPKs, pk) // append keys for output
|
||||
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, pk.Address(), "test_val1", true)
|
||||
require.NoError(t, err)
|
||||
appGenTxs = append(appGenTxs, appGenTx)
|
||||
}
|
||||
|
||||
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
// add some tokens to init accounts
|
||||
for _, addr := range initAddrs {
|
||||
accAuth := auth.NewBaseAccountWithAddress(addr)
|
||||
accAuth.Coins = sdk.Coins{{"steak", 100}}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
}
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
require.NoError(t, err)
|
||||
genDoc.AppStateJSON = appState
|
||||
|
||||
// LCD listen address
|
||||
var listenAddr string
|
||||
listenAddr, port, err = server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
|
||||
// XXX: need to set this so LCD knows the tendermint node address!
|
||||
viper.Set(client.FlagNode, config.RPC.ListenAddress)
|
||||
viper.Set(client.FlagChainID, genDoc.ChainID)
|
||||
|
||||
node, err := startTM(config, logger, genDoc, privVal, app)
|
||||
require.NoError(t, err)
|
||||
lcd, err := startLCD(logger, listenAddr, cdc)
|
||||
require.NoError(t, err)
|
||||
|
||||
//time.Sleep(time.Second)
|
||||
//tests.WaitForHeight(2, port)
|
||||
tests.WaitForStart(port)
|
||||
tests.WaitForHeight(1, port)
|
||||
|
||||
// for use in defer
|
||||
cleanup = func() {
|
||||
node.Stop()
|
||||
node.Wait()
|
||||
lcd.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Create & start in-process tendermint node with memdb
|
||||
// and in-process abci application.
|
||||
// TODO: need to clean up the WAL dir or enable it to be not persistent
|
||||
func startTM(tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, privVal tmtypes.PrivValidator, app abci.Application) (*nm.Node, error) {
|
||||
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil }
|
||||
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil }
|
||||
n, err := nm.NewNode(tmcfg,
|
||||
privVal,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
dbProvider,
|
||||
logger.With("module", "node"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wait for rpc
|
||||
tests.WaitForRPC(tmcfg.RPC.ListenAddress)
|
||||
|
||||
logger.Info("Tendermint running!")
|
||||
return n, err
|
||||
}
|
||||
|
||||
// start the LCD. note this blocks!
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
|
||||
handler := createHandler(cdc)
|
||||
return tmrpc.StartHTTPServer(listenAddr, handler, logger)
|
||||
}
|
||||
|
||||
// make a test lcd test request
|
||||
func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, path)
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
|
||||
output, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
return res, string(output)
|
||||
}
|
||||
@ -28,6 +28,7 @@ type sendBody struct {
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
}
|
||||
|
||||
var msgCdc = wire.NewCodec()
|
||||
@ -86,6 +87,9 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
|
||||
return
|
||||
}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
|
||||
|
||||
@ -26,6 +26,7 @@ type transferBody struct {
|
||||
Password string `json:"password"`
|
||||
SrcChainID string `json:"src_chain_id"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
}
|
||||
|
||||
// TransferRequestHandler - http request handler to transfer coins to a address
|
||||
@ -77,6 +78,9 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
|
||||
packet := ibc.NewIBCPacket(info.PubKey.Address(), to, m.Amount, m.SrcChainID, destChainID)
|
||||
msg := ibc.IBCTransferMsg{packet}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
|
||||
|
||||
@ -80,7 +80,7 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
||||
}
|
||||
}
|
||||
|
||||
// TODO inherit from Validator
|
||||
// TODO move exist next to validator struct for maintainability
|
||||
type StakeValidatorOutput struct {
|
||||
Owner string `json:"owner"` // in bech32
|
||||
PubKey string `json:"pub_key"` // in bech32
|
||||
|
||||
@ -40,6 +40,7 @@ type editDelegationsBody struct {
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegate []msgDelegateInput `json:"delegate"`
|
||||
Unbond []msgUnbondInput `json:"unbond"`
|
||||
}
|
||||
@ -121,6 +122,9 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
||||
i++
|
||||
}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign messages
|
||||
signedTxs := make([][]byte, len(messages[:]))
|
||||
for i, msg := range messages {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user