From 1b20adcd22a51b7efc551807e4532ca6b382b72b Mon Sep 17 00:00:00 2001 From: Rigel Date: Mon, 11 Jun 2018 18:12:37 -0700 Subject: [PATCH] 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 --- .gitignore | 3 +- CHANGELOG.md | 4 +- Makefile | 3 + client/keys/utils.go | 2 + client/lcd/helpers.go | 53 ---- client/lcd/lcd_test.go | 495 +++++++++++++--------------------- client/lcd/main_test.go | 38 --- client/lcd/root.go | 48 ++-- client/lcd/test_helpers.go | 234 ++++++++++++++++ x/bank/client/rest/sendtx.go | 4 + x/ibc/client/rest/transfer.go | 4 + x/stake/client/rest/query.go | 2 +- x/stake/client/rest/tx.go | 4 + 13 files changed, 464 insertions(+), 430 deletions(-) delete mode 100644 client/lcd/helpers.go delete mode 100644 client/lcd/main_test.go create mode 100644 client/lcd/test_helpers.go diff --git a/.gitignore b/.gitignore index 494e724528..da467c151f 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file +dependency-graph.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 16346def30..2485093891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index f7e2083c76..d6444b0b62 100644 --- a/Makefile +++ b/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 diff --git a/client/keys/utils.go b/client/keys/utils.go index d810dfa1f4..8b6cfcb351 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -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) diff --git a/client/lcd/helpers.go b/client/lcd/helpers.go deleted file mode 100644 index 14cd5c16c7..0000000000 --- a/client/lcd/helpers.go +++ /dev/null @@ -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 -} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 3565d99c31..48e2ad0a2d 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -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) diff --git a/client/lcd/main_test.go b/client/lcd/main_test.go deleted file mode 100644 index 9f0e2bd4f0..0000000000 --- a/client/lcd/main_test.go +++ /dev/null @@ -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) -} diff --git a/client/lcd/root.go b/client/lcd/root.go index 5071343021..c3ec75c964 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -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") diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go new file mode 100644 index 0000000000..c1898ff81c --- /dev/null +++ b/client/lcd/test_helpers.go @@ -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) +} diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 83ab3b843f..28a2946179 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -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) diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index d897c6e4f6..2b26b9f01c 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -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) diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 0da3260cb8..f8a2f00e5f 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -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 diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 33fe73c699..19eca4c447 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -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 {