From b558e50eb28b3a76124ab5c6ceb400b38f57b5a5 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 7 Dec 2018 17:33:52 -0800 Subject: [PATCH 01/27] Fix quoted json return --- client/keys/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/keys/list.go b/client/keys/list.go index f232fccff..9d01d500b 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -50,7 +50,7 @@ func QueryKeysRequestHandler(indent bool) http.HandlerFunc { } // an empty list will be JSONized as null, but we want to keep the empty list if len(infos) == 0 { - PostProcessResponse(w, cdc, "[]", indent) + PostProcessResponse(w, cdc, []string{}, indent) return } keysOutput, err := Bech32KeysOutput(infos) From be98f77aee148921ae5dd00cdfdcf6952ee11ada Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 7 Dec 2018 18:02:18 -0800 Subject: [PATCH 02/27] Add pending --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 73567c237..3be49eeb0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3,6 +3,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) + * [lcd] https://github.com/cosmos/cosmos-sdk/pull/3045 Fix quoted json return on GET /keys (keys list) * Gaia CLI (`gaiacli`) * [cli] [\#2595](https://github.com/cosmos/cosmos-sdk/issues/2595) Remove `keys new` in favor of `keys add` incorporating existing functionality with addition of key recovery functionality. From ac0a7c0a1d95206c3677e6d632035ddcd3dfdad6 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 10 Dec 2018 14:26:34 +0000 Subject: [PATCH 03/27] Move generate_only and simulate to POST body in REST txs Closes: #3056 --- client/lcd/lcd_test.go | 28 ++++++++++++++++------------ client/utils/rest.go | 30 +++++++----------------------- x/stake/client/rest/tx.go | 6 +++--- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 04453111a..34f8edbc9 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -279,29 +279,29 @@ func TestCoinSend(t *testing.T) { require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with negative gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, false, false) require.Equal(t, http.StatusBadRequest, res.StatusCode, body) // test failure with 0 gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with wrong adjustment - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 0.1, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 0.1, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 0, "?simulate=true") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 0, true, false) require.Equal(t, http.StatusOK, res.StatusCode, body) var responseBody struct { GasEstimate int64 `json:"gas_estimate"` } require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, false, false) require.Equal(t, http.StatusOK, res.StatusCode, body) } @@ -342,7 +342,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, "?generate_only=true") + res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, false, true) require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -897,7 +897,9 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { return acc } -func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) { +func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, + gasAdjustment float64, simulate, generateOnly bool) ( + res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address kb := client.MockKeyBase() @@ -935,11 +937,13 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc "password": "%s", "chain_id": "%s", "account_number":"%d", - "sequence":"%d" + "sequence": "%d", + "simulate": %v, + "generate_only": %v } - }`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence)) + }`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence, simulate, generateOnly)) - res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers%v", receiveAddr, queryStr), jsonStr) + res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), jsonStr) return } @@ -958,7 +962,7 @@ func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) } func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { - res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "") + res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, false, false) require.Equal(t, http.StatusOK, res.StatusCode, body) err := cdc.UnmarshalJSON([]byte(body), &resultTx) diff --git a/client/utils/rest.go b/client/utils/rest.go index c0a8c3c77..36ff05af8 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -4,7 +4,6 @@ import ( "fmt" "io/ioutil" "net/http" - "net/url" "strconv" "strings" @@ -17,11 +16,6 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) -const ( - queryArgDryRun = "simulate" - queryArgGenerateOnly = "generate_only" -) - //---------------------------------------- // Basic HTTP utilities @@ -39,18 +33,6 @@ func WriteSimulationResponse(w http.ResponseWriter, gas uint64) { w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas))) } -// HasDryRunArg returns true if the request's URL query contains the dry run -// argument and its value is set to "true". -func HasDryRunArg(r *http.Request) bool { - return urlQueryHasArg(r.URL, queryArgDryRun) -} - -// HasGenerateOnlyArg returns whether a URL's query "generate-only" parameter -// is set to "true". -func HasGenerateOnlyArg(r *http.Request) bool { - return urlQueryHasArg(r.URL, queryArgGenerateOnly) -} - // ParseInt64OrReturnBadRequest converts s to a int64 value. func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok bool) { var err error @@ -113,8 +95,6 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, txBldr authtxb.TxBuilder, return } -func urlQueryHasArg(url *url.URL, arg string) bool { return url.Query().Get(arg) == "true" } - //---------------------------------------- // Building / Sending utilities @@ -128,6 +108,8 @@ type BaseReq struct { Sequence uint64 `json:"sequence"` Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` + GenerateOnly bool `json:"generate_only"` + Simulate bool `json:"simulate"` } // Sanitize performs basic sanitization on a BaseReq object. @@ -140,6 +122,8 @@ func (br BaseReq) Sanitize() BaseReq { GasAdjustment: strings.TrimSpace(br.GasAdjustment), AccountNumber: br.AccountNumber, Sequence: br.Sequence, + GenerateOnly: br.GenerateOnly, + Simulate: br.Simulate, } } @@ -223,14 +207,14 @@ func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx c Sequence: baseReq.Sequence, } - if HasDryRunArg(r) || txBldr.SimulateGas { + if baseReq.Simulate || txBldr.SimulateGas { newBldr, err := EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, msgs) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - if HasDryRunArg(r) { + if baseReq.Simulate { WriteSimulationResponse(w, newBldr.Gas) return } @@ -238,7 +222,7 @@ func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx c txBldr = newBldr } - if HasGenerateOnlyArg(r) { + if baseReq.GenerateOnly { WriteGenerateStdTxResponse(w, txBldr, msgs) return } diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 437d40e77..5deb5b53c 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -219,14 +219,14 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte baseReq.Sequence++ - if utils.HasDryRunArg(r) || txBldr.SimulateGas { + if baseReq.Simulate || txBldr.SimulateGas { newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg}) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - if utils.HasDryRunArg(r) { + if baseReq.Simulate { utils.WriteSimulationResponse(w, newBldr.Gas) return } @@ -234,7 +234,7 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte txBldr = newBldr } - if utils.HasGenerateOnlyArg(r) { + if baseReq.GenerateOnly { utils.WriteGenerateStdTxResponse(w, txBldr, []sdk.Msg{msg}) return } From 24a1670cf04898d077c4f00d745ef6cc25d5f1d1 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 10 Dec 2018 14:27:25 +0000 Subject: [PATCH 04/27] Run make format --- baseapp/helpers.go | 3 ++- baseapp/options.go | 3 ++- baseapp/query_test.go | 3 ++- client/config.go | 3 ++- client/context/context.go | 7 ++++--- client/context/errors.go | 3 ++- client/context/query.go | 3 ++- client/keys.go | 3 ++- client/keys/delete.go | 3 ++- client/keys/mnemonic.go | 3 ++- client/keys/root.go | 3 ++- client/keys/show.go | 8 +++++--- client/keys/update.go | 8 +++++--- client/keys/utils.go | 9 +++++---- client/keys/utils_test.go | 6 ++++-- client/lcd/root.go | 13 +++++++------ client/lcd/test_helpers.go | 6 ++++-- client/rpc/block.go | 3 ++- client/rpc/root.go | 3 ++- client/rpc/status.go | 5 +++-- client/rpc/validators.go | 5 +++-- client/tx/broadcast.go | 3 ++- client/tx/query.go | 3 ++- client/utils/utils.go | 5 +++-- client/utils/utils_test.go | 5 +++-- cmd/cosmos-sdk-cli/cmd/init.go | 3 ++- cmd/gaia/app/app.go | 9 +++++---- cmd/gaia/app/app_test.go | 7 ++++--- cmd/gaia/app/benchmarks/txsize_test.go | 3 ++- cmd/gaia/app/export.go | 5 +++-- cmd/gaia/app/genesis.go | 3 ++- cmd/gaia/app/genesis_test.go | 7 ++++--- cmd/gaia/app/invariants.go | 3 ++- cmd/gaia/cmd/gaiadebug/main.go | 7 ++++--- cmd/gaia/init/collect.go | 11 ++++++----- cmd/gaia/init/genesis_accts.go | 9 +++++---- cmd/gaia/init/genesis_accts_test.go | 3 ++- cmd/gaia/init/gentx.go | 9 +++++---- cmd/gaia/init/init.go | 9 +++++---- cmd/gaia/init/init_test.go | 10 ++++++---- cmd/gaia/init/testnet.go | 3 ++- cmd/gaia/init/utils.go | 5 +++-- crypto/keys/codec.go | 3 ++- crypto/keys/keybase.go | 3 ++- crypto/keys/keybase_test.go | 3 ++- crypto/keys/mintkey/mintkey.go | 3 ++- crypto/keys/types.go | 3 ++- docs/_attic/sdk/core/examples/app2_test.go | 3 ++- docs/examples/basecoin/app/app.go | 11 ++++++----- docs/examples/basecoin/app/app_test.go | 9 +++++---- docs/examples/basecoin/cli_test/cli_test.go | 3 ++- docs/examples/basecoin/cmd/basecli/main.go | 5 +++-- docs/examples/basecoin/cmd/basecoind/main.go | 9 +++++---- docs/examples/democoin/app/app_test.go | 11 ++++++----- docs/examples/democoin/cli_test/cli_test.go | 3 ++- docs/examples/democoin/cmd/democoind/main.go | 3 ++- docs/examples/democoin/mock/validator.go | 3 ++- docs/examples/democoin/x/cool/app_test.go | 7 ++++--- docs/examples/democoin/x/pow/mine.go | 3 ++- server/config/config_test.go | 3 ++- server/export.go | 6 ++++-- server/init.go | 7 +++++-- server/mock/app.go | 3 ++- server/mock/app_test.go | 3 ++- server/test_helpers.go | 3 ++- server/tm_cmds.go | 8 +++++--- server/util.go | 9 +++++---- server/util_test.go | 3 ++- store/dbstoreadapter.go | 3 ++- store/list_test.go | 3 ++- store/multistoreproof_test.go | 3 ++- store/transientstore.go | 3 ++- types/context_test.go | 3 ++- types/decimal_test.go | 3 ++- types/errors.go | 3 ++- x/auth/account.go | 3 ++- x/auth/ante.go | 3 ++- x/auth/ante_test.go | 5 +++-- x/auth/client/cli/sign.go | 7 ++++--- x/auth/client/txbuilder/txbuilder_test.go | 3 ++- x/auth/keeper.go | 3 ++- x/auth/keeper_bench_test.go | 5 +++-- x/auth/keeper_test.go | 7 ++++--- x/auth/stdtx.go | 5 +++-- x/auth/stdtx_test.go | 3 ++- x/bank/client/cli/broadcast.go | 5 +++-- x/bank/client/cli/sendtx.go | 3 ++- x/bank/simulation/msgs.go | 3 ++- x/distribution/client/module_client.go | 5 +++-- x/distribution/keeper/allocation_test.go | 5 +++-- x/distribution/keeper/delegation_test.go | 3 ++- x/distribution/keeper/keeper_test.go | 3 ++- x/distribution/keeper/validator_test.go | 3 ++- x/distribution/types/dec_coin_test.go | 3 ++- x/distribution/types/delegator_info_test.go | 3 ++- x/distribution/types/fee_pool_test.go | 3 ++- x/distribution/types/test_common.go | 3 ++- x/distribution/types/total_accum_test.go | 3 ++- x/distribution/types/validator_info_test.go | 3 ++- x/gov/client/cli/query.go | 5 +++-- x/gov/client/cli/tx.go | 6 ++++-- x/gov/client/cli/tx_test.go | 5 +++-- x/gov/client/module_client.go | 5 +++-- x/gov/client/rest/rest.go | 3 ++- x/gov/depositsvotes.go | 3 ++- x/gov/endblocker_test.go | 3 ++- x/gov/querier.go | 3 ++- x/gov/querier_test.go | 5 +++-- x/gov/tally_test.go | 3 ++- x/mock/app.go | 9 +++++---- x/mock/app_test.go | 5 +++-- x/mock/simulation/invariants.go | 3 ++- x/mock/test_utils.go | 5 +++-- x/slashing/client/cli/tx.go | 3 ++- x/slashing/client/module_client.go | 5 +++-- x/slashing/client/rest/query.go | 3 ++- x/slashing/keeper.go | 3 ++- x/slashing/keeper_test.go | 5 +++-- x/slashing/tick.go | 3 ++- x/stake/client/cli/utils.go | 3 ++- x/stake/client/module_client.go | 5 +++-- x/stake/client/rest/query.go | 3 ++- x/stake/client/rest/utils.go | 3 ++- x/stake/genesis_test.go | 5 +++-- x/stake/keeper/key_test.go | 5 +++-- x/stake/keeper/slash_test.go | 3 ++- x/stake/keeper/validator_test.go | 3 ++- x/stake/querier/querier.go | 3 ++- x/stake/querier/querier_test.go | 5 +++-- x/stake/types/delegation_test.go | 3 ++- x/stake/types/msg.go | 3 ++- x/stake/types/msg_test.go | 3 ++- x/stake/types/pool_test.go | 3 ++- x/stake/types/test_utils.go | 3 ++- 134 files changed, 375 insertions(+), 231 deletions(-) diff --git a/baseapp/helpers.go b/baseapp/helpers.go index f3f1448bc..b6a0a3612 100644 --- a/baseapp/helpers.go +++ b/baseapp/helpers.go @@ -1,10 +1,11 @@ package baseapp import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/abci/server" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // nolint - Mostly for testing diff --git a/baseapp/options.go b/baseapp/options.go index 8d86933a0..6e4104e50 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -4,9 +4,10 @@ package baseapp import ( "fmt" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - dbm "github.com/tendermint/tendermint/libs/db" ) // File for storing in-package BaseApp optional functions, diff --git a/baseapp/query_test.go b/baseapp/query_test.go index d9d4001eb..fed7ae477 100644 --- a/baseapp/query_test.go +++ b/baseapp/query_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // Test that we can only query from the latest committed state. diff --git a/client/config.go b/client/config.go index 51d6da700..ab72ee78d 100644 --- a/client/config.go +++ b/client/config.go @@ -7,9 +7,10 @@ import ( "path" "strconv" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/pelletier/go-toml" "github.com/spf13/cobra" "github.com/spf13/viper" diff --git a/client/context/context.go b/client/context/context.go index 4b4407368..df30374de 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -13,14 +13,15 @@ import ( "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/keys" - cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" tmlite "github.com/tendermint/tendermint/lite" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" + + "github.com/cosmos/cosmos-sdk/client/keys" + cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/types" ) const ctxAccStoreName = "acc" diff --git a/client/context/errors.go b/client/context/errors.go index de96aaa18..f28454ca0 100644 --- a/client/context/errors.go +++ b/client/context/errors.go @@ -1,8 +1,9 @@ package context import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // ErrInvalidAccount returns a standardized error reflecting that a given diff --git a/client/context/query.go b/client/context/query.go index 486bd5186..0eff95d30 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,7 +10,6 @@ import ( "strings" - "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" @@ -18,6 +17,8 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/store" ) // GetNode returns an RPC client. If the context's client is not defined, an diff --git a/client/keys.go b/client/keys.go index a39b074b9..1fc594a2a 100644 --- a/client/keys.go +++ b/client/keys.go @@ -1,8 +1,9 @@ package client import ( - "github.com/cosmos/cosmos-sdk/crypto/keys" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/crypto/keys" ) // GetKeyBase initializes a keybase based on the given db. diff --git a/client/keys/delete.go b/client/keys/delete.go index e3fac4b7f..5f3ff4f09 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -10,10 +10,11 @@ import ( "github.com/spf13/viper" + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/crypto/keys" keyerror "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" - "github.com/gorilla/mux" "github.com/spf13/cobra" ) diff --git a/client/keys/mnemonic.go b/client/keys/mnemonic.go index 33270a087..32a6856bb 100644 --- a/client/keys/mnemonic.go +++ b/client/keys/mnemonic.go @@ -4,9 +4,10 @@ import ( "crypto/sha256" "fmt" - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" + bip39 "github.com/bartekn/go-bip39" ) diff --git a/client/keys/root.go b/client/keys/root.go index bca6f50e8..f747d84a0 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -1,9 +1,10 @@ package keys import ( - "github.com/cosmos/cosmos-sdk/client" "github.com/gorilla/mux" "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" ) // Commands registers a sub-tree of commands to interact with diff --git a/client/keys/show.go b/client/keys/show.go index d539eb3b7..9583dd07a 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -4,17 +4,19 @@ import ( "fmt" "net/http" - "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( diff --git a/client/keys/update.go b/client/keys/update.go index 2489bce12..e72da240c 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -5,12 +5,14 @@ import ( "fmt" "net/http" - "github.com/cosmos/cosmos-sdk/client" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" + "github.com/cosmos/cosmos-sdk/client" + keys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" ) func updateKeyCommand() *cobra.Command { diff --git a/client/keys/utils.go b/client/keys/utils.go index 0ac4a9c90..78899f5f8 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -5,14 +5,15 @@ import ( "net/http" "path/filepath" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/viper" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" ) // KeyDBName is the directory under root where we store the keys diff --git a/client/keys/utils_test.go b/client/keys/utils_test.go index 6b65bb55a..5f3dd4560 100644 --- a/client/keys/utils_test.go +++ b/client/keys/utils_test.go @@ -1,11 +1,13 @@ package keys import ( - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/stretchr/testify/require" "io/ioutil" "os" "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys" ) func TestGetKeyBaseLocks(t *testing.T) { diff --git a/client/lcd/root.go b/client/lcd/root.go index 00dab053e..ad7ba2ff0 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -7,12 +7,6 @@ import ( "net/http" "os" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/codec" - keybase "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/server" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/spf13/cobra" @@ -20,6 +14,13 @@ import ( "github.com/tendermint/tendermint/libs/log" rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + keybase "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + // Import statik for light client stuff _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 921dc96dd..87f57f385 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -14,9 +14,10 @@ import ( "strings" "testing" - stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/tendermint/tendermint/crypto/secp256k1" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" @@ -33,7 +34,6 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" abci "github.com/tendermint/tendermint/abci/types" tmcfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" @@ -48,6 +48,8 @@ import ( tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" + txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" diff --git a/client/rpc/block.go b/client/rpc/block.go index b2db545db..ee77fb5c1 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -8,11 +8,12 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/gorilla/mux" "github.com/spf13/cobra" "github.com/spf13/viper" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" + + "github.com/cosmos/cosmos-sdk/client/utils" ) //BlockCommand returns the verified block data for a given heights diff --git a/client/rpc/root.go b/client/rpc/root.go index 7bdcd8cd8..c8a98bc44 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -5,9 +5,10 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/spf13/viper" ) const ( diff --git a/client/rpc/status.go b/client/rpc/status.go index 0f81fc9c6..86bb1ef31 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -7,11 +7,12 @@ import ( "github.com/spf13/cobra" + "github.com/spf13/viper" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/spf13/viper" - ctypes "github.com/tendermint/tendermint/rpc/core/types" ) // StatusCommand returns the status of the network diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 6ec7ba615..9d401f69c 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -9,12 +9,13 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" + "github.com/spf13/viper" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/viper" - tmtypes "github.com/tendermint/tendermint/types" ) // TODO these next two functions feel kinda hacky based on their placement diff --git a/client/tx/broadcast.go b/client/tx/broadcast.go index 8346e1538..4080b68ec 100644 --- a/client/tx/broadcast.go +++ b/client/tx/broadcast.go @@ -3,10 +3,11 @@ package tx import ( "net/http" + "io/ioutil" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" - "io/ioutil" ) const ( diff --git a/client/tx/query.go b/client/tx/query.go index 3bbbf9a13..945701224 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -12,13 +12,14 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/spf13/viper" ) // QueryTxCmd implements the default command for a tx query. diff --git a/client/utils/utils.go b/client/utils/utils.go index 2d506e462..08a538eec 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -6,13 +6,14 @@ import ( "io" "os" + "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/libs/common" ) // CompleteAndBroadcastTxCli implements a utility function that facilitates diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go index b22a50806..23c665239 100644 --- a/client/utils/utils_test.go +++ b/client/utils/utils_test.go @@ -4,10 +4,11 @@ import ( "errors" "testing" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestParseQueryResponse(t *testing.T) { diff --git a/cmd/cosmos-sdk-cli/cmd/init.go b/cmd/cosmos-sdk-cli/cmd/init.go index fcf30b972..f8cf88b88 100644 --- a/cmd/cosmos-sdk-cli/cmd/init.go +++ b/cmd/cosmos-sdk-cli/cmd/init.go @@ -9,9 +9,10 @@ import ( "path/filepath" - "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" tmversion "github.com/tendermint/tendermint/version" + + "github.com/cosmos/cosmos-sdk/version" ) var remoteBasecoinPath = "github.com/cosmos/cosmos-sdk/docs/examples/basecoin" diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 270fe52ec..d97d25f0a 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -6,6 +6,11 @@ import ( "os" "sort" + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,10 +22,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" ) const ( diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index e356d6d6d..238f22966 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -4,6 +4,10 @@ import ( "os" "testing" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" distr "github.com/cosmos/cosmos-sdk/x/distribution" @@ -11,9 +15,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/cmd/gaia/app/benchmarks/txsize_test.go b/cmd/gaia/app/benchmarks/txsize_test.go index a401c16ca..d9862f22c 100644 --- a/cmd/gaia/app/benchmarks/txsize_test.go +++ b/cmd/gaia/app/benchmarks/txsize_test.go @@ -3,11 +3,12 @@ package app import ( "fmt" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/tendermint/tendermint/crypto/secp256k1" ) // This will fail half the time with the second output being 173 diff --git a/cmd/gaia/app/export.go b/cmd/gaia/app/export.go index a2aa42087..2b51c444b 100644 --- a/cmd/gaia/app/export.go +++ b/cmd/gaia/app/export.go @@ -4,6 +4,9 @@ import ( "encoding/json" "fmt" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -12,8 +15,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/slashing" stake "github.com/cosmos/cosmos-sdk/x/stake" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" ) // export the state of gaia for a genesis file diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index ee3ecfcd6..9c92f60ba 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -10,6 +10,8 @@ import ( "sort" "strings" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -19,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - tmtypes "github.com/tendermint/tendermint/types" ) var ( diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 1b1c19646..425962fa3 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -7,13 +7,14 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" tmtypes "github.com/tendermint/tendermint/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" ) var ( diff --git a/cmd/gaia/app/invariants.go b/cmd/gaia/app/invariants.go index 4a60a41ae..4582457fc 100644 --- a/cmd/gaia/app/invariants.go +++ b/cmd/gaia/app/invariants.go @@ -4,12 +4,13 @@ import ( "fmt" "time" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/mock/simulation" stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation" - abci "github.com/tendermint/tendermint/abci/types" ) func (app *GaiaApp) runtimeInvariants() []simulation.Invariant { diff --git a/cmd/gaia/cmd/gaiadebug/main.go b/cmd/gaia/cmd/gaiadebug/main.go index 5117d0aa1..59e324966 100644 --- a/cmd/gaia/cmd/gaiadebug/main.go +++ b/cmd/gaia/cmd/gaiadebug/main.go @@ -10,12 +10,13 @@ import ( "strconv" "strings" - gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/cobra" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + + gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) func init() { diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index cdfc1688c..da97e4626 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -4,17 +4,18 @@ import ( "encoding/json" "path/filepath" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/auth" ) type initConfig struct { diff --git a/cmd/gaia/init/genesis_accts.go b/cmd/gaia/init/genesis_accts.go index 04c1f6283..4f5043877 100644 --- a/cmd/gaia/init/genesis_accts.go +++ b/cmd/gaia/init/genesis_accts.go @@ -4,15 +4,16 @@ import ( "encoding/json" "fmt" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/common" ) // AddGenesisAccountCmd returns add-genesis-account cobra Command diff --git a/cmd/gaia/init/genesis_accts_test.go b/cmd/gaia/init/genesis_accts_test.go index 49634a90c..42a36b263 100644 --- a/cmd/gaia/init/genesis_accts_test.go +++ b/cmd/gaia/init/genesis_accts_test.go @@ -1,9 +1,10 @@ package init import ( + "testing" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - "testing" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 0a4eb8034..c8a7c05d2 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -11,6 +11,11 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" @@ -23,10 +28,6 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/common" ) const ( diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 19179233a..7690d5336 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -6,15 +6,16 @@ import ( "os" "path/filepath" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" ) const ( diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index 1eeba66ae..faf324e6c 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -8,17 +8,19 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/stretchr/testify/require" abciServer "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/mock" + "github.com/spf13/viper" ) diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 73a7cea14..cfa3e6396 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -16,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" @@ -24,6 +23,8 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/cosmos/cosmos-sdk/server" ) var ( diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go index 58edc9b2a..14c625d7f 100644 --- a/cmd/gaia/init/utils.go +++ b/cmd/gaia/init/utils.go @@ -6,8 +6,6 @@ import ( "io/ioutil" "time" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/codec" amino "github.com/tendermint/go-amino" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" @@ -15,6 +13,9 @@ import ( "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" ) // ExportGenesisFile creates and writes the genesis configuration to disk. An diff --git a/crypto/keys/codec.go b/crypto/keys/codec.go index 80a13539a..f6c1a013d 100644 --- a/crypto/keys/codec.go +++ b/crypto/keys/codec.go @@ -1,9 +1,10 @@ package keys import ( - ccrypto "github.com/cosmos/cosmos-sdk/crypto" amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto/encoding/amino" + + ccrypto "github.com/cosmos/cosmos-sdk/crypto" ) var cdc = amino.NewCodec() diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 982a4e726..e202cd6d8 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -15,11 +15,12 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" tmcrypto "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" ) var _ Keybase = dbKeybase{} diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 3c7f98060..bc7783b67 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -13,8 +13,9 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/cosmos/cosmos-sdk/types" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/types" ) func init() { diff --git a/crypto/keys/mintkey/mintkey.go b/crypto/keys/mintkey/mintkey.go index 35cb9ccab..3b06415e2 100644 --- a/crypto/keys/mintkey/mintkey.go +++ b/crypto/keys/mintkey/mintkey.go @@ -11,8 +11,9 @@ import ( "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" ) const ( diff --git a/crypto/keys/types.go b/crypto/keys/types.go index b4b328516..14d050961 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -1,9 +1,10 @@ package keys import ( - ccrypto "github.com/cosmos/cosmos-sdk/crypto" "github.com/tendermint/tendermint/crypto" + ccrypto "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" "github.com/cosmos/cosmos-sdk/types" ) diff --git a/docs/_attic/sdk/core/examples/app2_test.go b/docs/_attic/sdk/core/examples/app2_test.go index b7c94bbcf..0712e7add 100644 --- a/docs/_attic/sdk/core/examples/app2_test.go +++ b/docs/_attic/sdk/core/examples/app2_test.go @@ -3,9 +3,10 @@ package app import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) diff --git a/docs/examples/basecoin/app/app.go b/docs/examples/basecoin/app/app.go index f534c9128..a469e0b2a 100644 --- a/docs/examples/basecoin/app/app.go +++ b/docs/examples/basecoin/app/app.go @@ -4,6 +4,12 @@ import ( "encoding/json" "os" + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" @@ -11,11 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" ) const ( diff --git a/docs/examples/basecoin/app/app_test.go b/docs/examples/basecoin/app/app_test.go index 64bc6a86e..4dae71194 100644 --- a/docs/examples/basecoin/app/app_test.go +++ b/docs/examples/basecoin/app/app_test.go @@ -4,15 +4,16 @@ import ( "os" "testing" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.GenesisState, error) { diff --git a/docs/examples/basecoin/cli_test/cli_test.go b/docs/examples/basecoin/cli_test/cli_test.go index 635b54c3c..7e6a648df 100644 --- a/docs/examples/basecoin/cli_test/cli_test.go +++ b/docs/examples/basecoin/cli_test/cli_test.go @@ -6,10 +6,11 @@ import ( "os" "testing" + "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" - "github.com/stretchr/testify/require" ) var ( diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index 6e274123e..fe681a3ad 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -9,6 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/libs/cli" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -20,8 +23,6 @@ import ( slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" - "github.com/spf13/cobra" - "github.com/tendermint/tendermint/libs/cli" ) const ( diff --git a/docs/examples/basecoin/cmd/basecoind/main.go b/docs/examples/basecoin/cmd/basecoind/main.go index 383a843b2..9cb246671 100644 --- a/docs/examples/basecoin/cmd/basecoind/main.go +++ b/docs/examples/basecoin/cmd/basecoind/main.go @@ -11,10 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" @@ -23,6 +19,11 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/server" ) const ( diff --git a/docs/examples/democoin/app/app_test.go b/docs/examples/democoin/app/app_test.go index 93cef936c..200103466 100644 --- a/docs/examples/democoin/app/app_test.go +++ b/docs/examples/democoin/app/app_test.go @@ -4,16 +4,17 @@ import ( "os" "testing" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/docs/examples/democoin/types" - "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/docs/examples/democoin/types" + "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error { diff --git a/docs/examples/democoin/cli_test/cli_test.go b/docs/examples/democoin/cli_test/cli_test.go index 9a3cba0df..1aa9d94ac 100644 --- a/docs/examples/democoin/cli_test/cli_test.go +++ b/docs/examples/democoin/cli_test/cli_test.go @@ -8,9 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" - "github.com/stretchr/testify/require" ) var ( diff --git a/docs/examples/democoin/cmd/democoind/main.go b/docs/examples/democoin/cmd/democoind/main.go index 8f52340f4..f0553ee5d 100644 --- a/docs/examples/democoin/cmd/democoind/main.go +++ b/docs/examples/democoin/cmd/democoind/main.go @@ -6,11 +6,12 @@ import ( "io" "os" - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" diff --git a/docs/examples/democoin/mock/validator.go b/docs/examples/democoin/mock/validator.go index 1d10c48b2..ee8e497cb 100644 --- a/docs/examples/democoin/mock/validator.go +++ b/docs/examples/democoin/mock/validator.go @@ -3,8 +3,9 @@ package mock import ( "bytes" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // Validator implements sdk.Validator diff --git a/docs/examples/democoin/x/cool/app_test.go b/docs/examples/democoin/x/cool/app_test.go index 3725f7b9b..7e9e29f24 100644 --- a/docs/examples/democoin/x/cool/app_test.go +++ b/docs/examples/democoin/x/cool/app_test.go @@ -3,13 +3,14 @@ package cool import ( "testing" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" bank "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" ) var ( diff --git a/docs/examples/democoin/x/pow/mine.go b/docs/examples/democoin/x/pow/mine.go index bf1c64cd4..3c24c1264 100644 --- a/docs/examples/democoin/x/pow/mine.go +++ b/docs/examples/democoin/x/pow/mine.go @@ -5,8 +5,9 @@ import ( "math" "strconv" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // generate the mine message diff --git a/server/config/config_test.go b/server/config/config_test.go index e4d552ad2..d68d84415 100644 --- a/server/config/config_test.go +++ b/server/config/config_test.go @@ -3,8 +3,9 @@ package config import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestDefaultConfig(t *testing.T) { diff --git a/server/export.go b/server/export.go index 7b5ba4a69..aa30597da 100644 --- a/server/export.go +++ b/server/export.go @@ -7,10 +7,12 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/codec" - tmtypes "github.com/tendermint/tendermint/types" "io/ioutil" "path" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" ) const ( diff --git a/server/init.go b/server/init.go index e1655c27e..dd9e7b890 100644 --- a/server/init.go +++ b/server/init.go @@ -4,15 +4,18 @@ import ( "encoding/json" "errors" "fmt" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/crypto/keys" + + tmtypes "github.com/tendermint/tendermint/types" + clkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - tmtypes "github.com/tendermint/tendermint/types" ) // SimpleGenTx is a simple genesis tx diff --git a/server/mock/app.go b/server/mock/app.go index 9e5af4498..2b0b8ed5e 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -3,9 +3,10 @@ package mock import ( "encoding/json" "fmt" - "github.com/tendermint/tendermint/types" "path/filepath" + "github.com/tendermint/tendermint/types" + abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" diff --git a/server/mock/app_test.go b/server/mock/app_test.go index a5f2a078b..707c22c85 100644 --- a/server/mock/app_test.go +++ b/server/mock/app_test.go @@ -1,9 +1,10 @@ package mock import ( - "github.com/tendermint/tendermint/types" "testing" + "github.com/tendermint/tendermint/types" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" diff --git a/server/test_helpers.go b/server/test_helpers.go index 4347bad6c..148f63a48 100644 --- a/server/test_helpers.go +++ b/server/test_helpers.go @@ -2,12 +2,13 @@ package server import ( "fmt" - "github.com/cosmos/cosmos-sdk/client" "io/ioutil" "net" "os" "testing" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/viper" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 5aeacf92f..33994b0cb 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -3,15 +3,17 @@ package server import ( "fmt" - "github.com/cosmos/cosmos-sdk/codec" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/codec" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" ) // ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout diff --git a/server/util.go b/server/util.go index 3d4a9d0b6..5c07d1b46 100644 --- a/server/util.go +++ b/server/util.go @@ -13,15 +13,16 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server/config" - "github.com/cosmos/cosmos-sdk/version" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" tmflags "github.com/tendermint/tendermint/libs/cli/flags" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/version" ) // server context diff --git a/server/util_test.go b/server/util_test.go index 2c16759c9..fb4e73259 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -4,8 +4,9 @@ import ( "encoding/json" "testing" - "github.com/cosmos/cosmos-sdk/codec" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" ) func TestInsertKeyJSON(t *testing.T) { diff --git a/store/dbstoreadapter.go b/store/dbstoreadapter.go index 8f4a1583c..76e673de5 100644 --- a/store/dbstoreadapter.go +++ b/store/dbstoreadapter.go @@ -3,8 +3,9 @@ package store import ( "io" - sdk "github.com/cosmos/cosmos-sdk/types" dbm "github.com/tendermint/tendermint/libs/db" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // Wrapper type for dbm.Db with implementation of KVStore diff --git a/store/list_test.go b/store/list_test.go index 396e2d1a1..6767457cd 100644 --- a/store/list_test.go +++ b/store/list_test.go @@ -4,8 +4,9 @@ import ( "math/rand" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestList(t *testing.T) { diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index db3b65cad..0f80657b8 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -3,10 +3,11 @@ package store import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestVerifyIAVLStoreQueryProof(t *testing.T) { diff --git a/store/transientstore.go b/store/transientstore.go index a3ce89631..63b154c01 100644 --- a/store/transientstore.go +++ b/store/transientstore.go @@ -1,8 +1,9 @@ package store import ( - sdk "github.com/cosmos/cosmos-sdk/types" dbm "github.com/tendermint/tendermint/libs/db" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var _ KVStore = (*transientStore)(nil) diff --git a/types/context_test.go b/types/context_test.go index 0ab6c8dfc..3ccea2a8a 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -8,9 +8,10 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) type MockLogger struct { diff --git a/types/decimal_test.go b/types/decimal_test.go index b35894771..fa6442f3a 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -6,8 +6,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/cosmos/cosmos-sdk/codec" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" ) // create a decimal from a decimal string (ex. "1234.5678") diff --git a/types/errors.go b/types/errors.go index 46436531e..f659f2954 100644 --- a/types/errors.go +++ b/types/errors.go @@ -4,9 +4,10 @@ import ( "fmt" "strings" - "github.com/cosmos/cosmos-sdk/codec" cmn "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/codec" + abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/x/auth/account.go b/x/auth/account.go index 0fa601acc..f647601ca 100644 --- a/x/auth/account.go +++ b/x/auth/account.go @@ -3,9 +3,10 @@ package auth import ( "errors" + "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto" ) // Account is an interface used to store coins at a given address within state. diff --git a/x/auth/ante.go b/x/auth/ante.go index 3c8a0f9aa..91789dd22 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -5,10 +5,11 @@ import ( "encoding/hex" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 20fec8896..9d76107fe 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -5,8 +5,6 @@ import ( "strings" "testing" - codec "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" @@ -14,6 +12,9 @@ import ( "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/libs/log" + + codec "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 39d9b6694..842299df8 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -6,15 +6,16 @@ import ( "io/ioutil" "os" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/go-amino" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/go-amino" ) const ( diff --git a/x/auth/client/txbuilder/txbuilder_test.go b/x/auth/client/txbuilder/txbuilder_test.go index 4ff472fef..7b0a281d6 100644 --- a/x/auth/client/txbuilder/txbuilder_test.go +++ b/x/auth/client/txbuilder/txbuilder_test.go @@ -6,11 +6,12 @@ import ( "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/tendermint/tendermint/crypto/ed25519" ) var ( diff --git a/x/auth/keeper.go b/x/auth/keeper.go index 18e82c206..bf8b92da6 100644 --- a/x/auth/keeper.go +++ b/x/auth/keeper.go @@ -1,9 +1,10 @@ package auth import ( + "github.com/tendermint/tendermint/crypto" + codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto" ) var ( diff --git a/x/auth/keeper_bench_test.go b/x/auth/keeper_bench_test.go index 5ca414048..413cd6afd 100644 --- a/x/auth/keeper_bench_test.go +++ b/x/auth/keeper_bench_test.go @@ -3,10 +3,11 @@ package auth import ( "testing" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) func BenchmarkAccountMapperGetAccountFound(b *testing.B) { diff --git a/x/auth/keeper_test.go b/x/auth/keeper_test.go index b0ba2e74b..ad05c0f8e 100644 --- a/x/auth/keeper_test.go +++ b/x/auth/keeper_test.go @@ -3,13 +3,14 @@ package auth import ( "testing" - codec "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + + codec "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" ) func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index 59edf7c13..053b405c9 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -4,10 +4,11 @@ import ( "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/multisig" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) var _ sdk.Tx = (*StdTx)(nil) diff --git a/x/auth/stdtx_test.go b/x/auth/stdtx_test.go index 1f2fefaca..da1fac179 100644 --- a/x/auth/stdtx_test.go +++ b/x/auth/stdtx_test.go @@ -5,12 +5,13 @@ import ( "strings" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index 126668364..1bcd811cd 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -4,11 +4,12 @@ import ( "io/ioutil" "os" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" ) // GetSignCommand returns the sign command diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 1a7c444af..e61d6eb56 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -1,6 +1,8 @@ package cli import ( + "os" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -8,7 +10,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" bankClient "github.com/cosmos/cosmos-sdk/x/bank/client" - "os" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index 78b1f1945..96df52f56 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -6,13 +6,14 @@ import ( "math/big" "math/rand" + "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/mock/simulation" - "github.com/tendermint/tendermint/crypto" ) // SingleInputSendTx tests and runs a single msg send w/ auth, with one input and one output, where both diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go index ba725e1f8..69734b08d 100644 --- a/x/distribution/client/module_client.go +++ b/x/distribution/client/module_client.go @@ -1,10 +1,11 @@ package client import ( - "github.com/cosmos/cosmos-sdk/client" - distCmds "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/spf13/cobra" amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/client" + distCmds "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" ) // ModuleClient exports all client functionality from this module diff --git a/x/distribution/keeper/allocation_test.go b/x/distribution/keeper/allocation_test.go index 7d05e82b1..35ad25e66 100644 --- a/x/distribution/keeper/allocation_test.go +++ b/x/distribution/keeper/allocation_test.go @@ -3,10 +3,11 @@ package keeper import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake" ) func TestAllocateTokensBasic(t *testing.T) { diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 455ad14e8..67606cc84 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -3,9 +3,10 @@ package keeper import ( "testing" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/stretchr/testify/require" ) func TestWithdrawDelegationRewardBasic(t *testing.T) { diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index f8eb0925d..a8c378424 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -3,9 +3,10 @@ package keeper import ( "testing" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/stretchr/testify/require" ) func TestSetGetPreviousProposerConsAddr(t *testing.T) { diff --git a/x/distribution/keeper/validator_test.go b/x/distribution/keeper/validator_test.go index fc9331959..58079241c 100644 --- a/x/distribution/keeper/validator_test.go +++ b/x/distribution/keeper/validator_test.go @@ -3,9 +3,10 @@ package keeper import ( "testing" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/stretchr/testify/require" ) func TestWithdrawValidatorRewardsAllNoDelegator(t *testing.T) { diff --git a/x/distribution/types/dec_coin_test.go b/x/distribution/types/dec_coin_test.go index 5a326fa17..9b942f07c 100644 --- a/x/distribution/types/dec_coin_test.go +++ b/x/distribution/types/dec_coin_test.go @@ -3,9 +3,10 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestPlusDecCoin(t *testing.T) { diff --git a/x/distribution/types/delegator_info_test.go b/x/distribution/types/delegator_info_test.go index e15ad2930..5619fb4d1 100644 --- a/x/distribution/types/delegator_info_test.go +++ b/x/distribution/types/delegator_info_test.go @@ -3,8 +3,9 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestWithdrawRewards(t *testing.T) { diff --git a/x/distribution/types/fee_pool_test.go b/x/distribution/types/fee_pool_test.go index 73bda52fa..ceb908d7f 100644 --- a/x/distribution/types/fee_pool_test.go +++ b/x/distribution/types/fee_pool_test.go @@ -3,8 +3,9 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestUpdateTotalValAccum(t *testing.T) { diff --git a/x/distribution/types/test_common.go b/x/distribution/types/test_common.go index b77efd46c..480244ac3 100644 --- a/x/distribution/types/test_common.go +++ b/x/distribution/types/test_common.go @@ -1,9 +1,10 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( diff --git a/x/distribution/types/total_accum_test.go b/x/distribution/types/total_accum_test.go index 81f80a154..3984612ad 100644 --- a/x/distribution/types/total_accum_test.go +++ b/x/distribution/types/total_accum_test.go @@ -3,8 +3,9 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestTotalAccumUpdateForNewHeight(t *testing.T) { diff --git a/x/distribution/types/validator_info_test.go b/x/distribution/types/validator_info_test.go index 24c3eaee4..119eb6343 100644 --- a/x/distribution/types/validator_info_test.go +++ b/x/distribution/types/validator_info_test.go @@ -3,9 +3,10 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestTakeFeePoolRewards(t *testing.T) { diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index 4f0c86fea..06dabfcdd 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -5,13 +5,14 @@ import ( "strconv" "strings" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) // GetCmdQueryProposal implements the query proposal command. diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 58ce91352..74c311ef4 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -5,21 +5,23 @@ import ( "os" "strconv" + "github.com/pkg/errors" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/pkg/errors" "encoding/json" "io/ioutil" "strings" - govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/spf13/cobra" "github.com/spf13/viper" + + govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" ) const ( diff --git a/x/gov/client/cli/tx_test.go b/x/gov/client/cli/tx_test.go index e3aed05ff..73df8c290 100644 --- a/x/gov/client/cli/tx_test.go +++ b/x/gov/client/cli/tx_test.go @@ -1,10 +1,11 @@ package cli import ( - "github.com/spf13/viper" - "github.com/stretchr/testify/require" "io/ioutil" "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" ) func TestParseSubmitProposalFlags(t *testing.T) { diff --git a/x/gov/client/module_client.go b/x/gov/client/module_client.go index 456ddfbe7..1f67c9c07 100644 --- a/x/gov/client/module_client.go +++ b/x/gov/client/module_client.go @@ -1,10 +1,11 @@ package client import ( - "github.com/cosmos/cosmos-sdk/client" - govCli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" "github.com/spf13/cobra" amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/client" + govCli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" ) // ModuleClient exports all client functionality from this module diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 72abdb214..53deffbbe 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -10,9 +10,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" - govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/gorilla/mux" "github.com/pkg/errors" + + govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" ) // REST Variable names diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index 8e22f245d..3e2ac5673 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -4,8 +4,9 @@ import ( "encoding/json" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // Vote diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 47e903758..622d0968f 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -6,9 +6,10 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" ) func TestTickExpiredDepositPeriod(t *testing.T) { diff --git a/x/gov/querier.go b/x/gov/querier.go index cde85bf94..8b985929e 100644 --- a/x/gov/querier.go +++ b/x/gov/querier.go @@ -3,9 +3,10 @@ package gov import ( "fmt" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) // query endpoints supported by the governance Querier diff --git a/x/gov/querier_test.go b/x/gov/querier_test.go index 9ee71323e..01b611e19 100644 --- a/x/gov/querier_test.go +++ b/x/gov/querier_test.go @@ -4,10 +4,11 @@ import ( "strings" "testing" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (DepositParams, VotingParams, TallyParams) { diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 86564ed42..fc5cac910 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -5,11 +5,12 @@ import ( "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) diff --git a/x/mock/app.go b/x/mock/app.go index 9251a95c0..066ac93dc 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -7,16 +7,17 @@ import ( "os" "sort" - bam "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) const chainID = "" diff --git a/x/mock/app_test.go b/x/mock/app_test.go index 1af0e4a03..098126d89 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -3,10 +3,11 @@ package mock import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) const msgRoute = "testMsg" diff --git a/x/mock/simulation/invariants.go b/x/mock/simulation/invariants.go index a3ae5f3c3..d30b67be9 100644 --- a/x/mock/simulation/invariants.go +++ b/x/mock/simulation/invariants.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) // An Invariant is a function which tests a particular invariant. diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go index 4d5fe05f3..9c24967ab 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -5,11 +5,12 @@ import ( "math/rand" "testing" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" ) // BigInterval is a representation of the interval [lo, hi), where diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 513710ce4..2e97414bd 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -1,13 +1,14 @@ package cli import ( + "os" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/slashing" - "os" "github.com/spf13/cobra" ) diff --git a/x/slashing/client/module_client.go b/x/slashing/client/module_client.go index 82efb5afe..2d7c6b6eb 100644 --- a/x/slashing/client/module_client.go +++ b/x/slashing/client/module_client.go @@ -1,10 +1,11 @@ package client import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" "github.com/spf13/cobra" amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" ) // ModuleClient exports all client functionality from this module diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index e55b6b9be..a95c01acf 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -3,12 +3,13 @@ package rest import ( "net/http" + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/gorilla/mux" ) func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 61d366172..5137b1198 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -4,11 +4,12 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" stake "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/tendermint/tendermint/crypto" ) // Keeper of the slashing store diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index dd32ea3f8..fdf409c23 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -4,10 +4,11 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake" ) // Have to change these parameters for tests diff --git a/x/slashing/tick.go b/x/slashing/tick.go index 03bd094af..41af4b9ae 100644 --- a/x/slashing/tick.go +++ b/x/slashing/tick.go @@ -4,9 +4,10 @@ import ( "encoding/binary" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // slashing begin block functionality diff --git a/x/stake/client/cli/utils.go b/x/stake/client/cli/utils.go index 502cb11ec..848e1725d 100644 --- a/x/stake/client/cli/utils.go +++ b/x/stake/client/cli/utils.go @@ -1,12 +1,13 @@ package cli import ( + "github.com/pkg/errors" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/pkg/errors" ) func getShares( diff --git a/x/stake/client/module_client.go b/x/stake/client/module_client.go index 5a08668dc..03a7c25e2 100644 --- a/x/stake/client/module_client.go +++ b/x/stake/client/module_client.go @@ -1,10 +1,11 @@ package client import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/spf13/cobra" amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/stake/client/cli" ) // ModuleClient exports all client functionality from this module diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 5788346f2..085d7e4e2 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -1,10 +1,11 @@ package rest import ( - "github.com/cosmos/cosmos-sdk/x/stake" "net/http" "strings" + "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/utils" diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index 7f6edc193..d9ae457cf 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -4,6 +4,8 @@ import ( "fmt" "net/http" + "github.com/gorilla/mux" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/utils" @@ -11,7 +13,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" - "github.com/gorilla/mux" rpcclient "github.com/tendermint/tendermint/rpc/client" ) diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index dfe5b7d1b..f747ca7a7 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -8,11 +8,12 @@ import ( "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" ) func TestInitGenesis(t *testing.T) { diff --git a/x/stake/keeper/key_test.go b/x/stake/keeper/key_test.go index 0658e4b24..f994c3920 100644 --- a/x/stake/keeper/key_test.go +++ b/x/stake/keeper/key_test.go @@ -5,10 +5,11 @@ import ( "math/big" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/stretchr/testify/assert" "github.com/tendermint/tendermint/crypto/ed25519" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" ) var ( diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 9f89dd210..94418a962 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -6,9 +6,10 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" ) // TODO integrate with test_common.go helper (CreateTestInput) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 71b0edcd1..e328537a5 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -5,9 +5,10 @@ import ( "testing" "time" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/x/stake/querier/querier.go b/x/stake/querier/querier.go index 13ff97ef3..70627f0b1 100644 --- a/x/stake/querier/querier.go +++ b/x/stake/querier/querier.go @@ -1,11 +1,12 @@ package querier import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" - abci "github.com/tendermint/tendermint/abci/types" ) // query endpoints supported by the staking Querier diff --git a/x/stake/querier/querier_test.go b/x/stake/querier/querier_test.go index c13a37897..525f3691a 100644 --- a/x/stake/querier/querier_test.go +++ b/x/stake/querier/querier_test.go @@ -3,12 +3,13 @@ package querier import ( "testing" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" ) var ( diff --git a/x/stake/types/delegation_test.go b/x/stake/types/delegation_test.go index e75212257..ee7d81bf5 100644 --- a/x/stake/types/delegation_test.go +++ b/x/stake/types/delegation_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestDelegationEqual(t *testing.T) { diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 63d0afb5f..a6692a7f3 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -3,8 +3,9 @@ package types import ( "bytes" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // name to identify transaction routes diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index 4b4055fca..e5cb8a9f0 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -5,8 +5,9 @@ import ( "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( diff --git a/x/stake/types/pool_test.go b/x/stake/types/pool_test.go index 4541edd3d..f877df0b1 100644 --- a/x/stake/types/pool_test.go +++ b/x/stake/types/pool_test.go @@ -3,8 +3,9 @@ package types import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestPoolEqual(t *testing.T) { diff --git a/x/stake/types/test_utils.go b/x/stake/types/test_utils.go index b9d77ce9f..548cd316a 100644 --- a/x/stake/types/test_utils.go +++ b/x/stake/types/test_utils.go @@ -1,9 +1,10 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( From 2e05d4e3d731188018a14c5cdfb60c55ce82ea79 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 10 Dec 2018 14:36:20 +0000 Subject: [PATCH 05/27] drop extra else block, outdent its block, and make linter happy --- x/stake/keeper/test_common.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index f52b41c44..c3ca811cd 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -227,15 +227,14 @@ func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Vali panic("validator expected but not found") } return validator - } else { - cachectx, _ := ctx.CacheContext() - keeper.ApplyAndReturnValidatorSetUpdates(cachectx) - validator, found := keeper.GetValidator(cachectx, validator.OperatorAddr) - if !found { - panic("validator expected but not found") - } - return validator } + cachectx, _ := ctx.CacheContext() + keeper.ApplyAndReturnValidatorSetUpdates(cachectx) + validator, found := keeper.GetValidator(cachectx, validator.OperatorAddr) + if !found { + panic("validator expected but not found") + } + return validator } func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { From b4528d2a5d864394f09638647be6facf0a7e285d Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 10 Dec 2018 14:39:25 +0000 Subject: [PATCH 06/27] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 95ecba5b5..0bf61e800 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3,6 +3,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) + * [\#3056](https://github.com/cosmos/cosmos-sdk/pull/3056) `generate_only` and `simulate` have moved from query arguments to POST requests body. * Gaia CLI (`gaiacli`) * [cli] [\#2595](https://github.com/cosmos/cosmos-sdk/issues/2595) Remove `keys new` in favor of `keys add` incorporating existing functionality with addition of key recovery functionality. From 7d55bd1a3653c42655f13f80847c5525c6642b85 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 10 Dec 2018 16:13:26 +0100 Subject: [PATCH 07/27] Merge PR #3063: Fix linter --- x/stake/keeper/test_common.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index f52b41c44..c3ca811cd 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -227,15 +227,14 @@ func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Vali panic("validator expected but not found") } return validator - } else { - cachectx, _ := ctx.CacheContext() - keeper.ApplyAndReturnValidatorSetUpdates(cachectx) - validator, found := keeper.GetValidator(cachectx, validator.OperatorAddr) - if !found { - panic("validator expected but not found") - } - return validator } + cachectx, _ := ctx.CacheContext() + keeper.ApplyAndReturnValidatorSetUpdates(cachectx) + validator, found := keeper.GetValidator(cachectx, validator.OperatorAddr) + if !found { + panic("validator expected but not found") + } + return validator } func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { From da7f459d44b048a1ee1a3ada107a573be8622cc7 Mon Sep 17 00:00:00 2001 From: dongsamb Date: Tue, 11 Dec 2018 00:29:10 +0900 Subject: [PATCH 08/27] Merge PR #3050: Add generate-only option to withdraw-rewards --- x/distribution/client/cli/tx.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index ee82498e8..0a51b2e8e 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -3,6 +3,7 @@ package cli import ( "fmt" + "os" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -88,6 +89,10 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { msg = types.NewMsgWithdrawDelegatorRewardsAll(delAddr) } + if cliCtx.GenerateOnly { + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) + } + // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, From 4f34cb7c1e917224f28c12ff052e98dd9d311084 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 10 Dec 2018 16:00:38 +0000 Subject: [PATCH 09/27] Update LCD swagger docs --- client/lcd/swagger-ui/swagger.yaml | 66 +++--------------------------- 1 file changed, 6 insertions(+), 60 deletions(-) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 82e7c3429..0ecbe505f 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -376,16 +376,6 @@ paths: produces: - application/json parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - in: path name: address description: Account address in bech32 format @@ -638,16 +628,6 @@ paths: post: summary: Submit delegation parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - in: body name: delegation description: The password of the account to remove from the KMS @@ -1123,16 +1103,6 @@ paths: tags: - ICS23 parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - type: string description: Bech32 validator address name: validatorAddr @@ -1169,16 +1139,6 @@ paths: tags: - ICS22 parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - description: valid value of `"proposal_type"` can be `"text"`, `"parameter_change"`, `"software_upgrade"` name: post_proposal_body in: body @@ -1257,16 +1217,6 @@ paths: tags: - ICS22 parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - type: string description: proposal id name: proposalId @@ -1355,16 +1305,6 @@ paths: tags: - ICS22 parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - type: string description: proposal id name: proposalId @@ -1888,6 +1828,12 @@ definitions: gas_adjustment: type: string example: "1.2" + generate_only: + type: boolean + example: false + simulate: + type: boolean + example: true TendermintValidator: type: object properties: From 2ce41760e2c9cff38ed2fa191c949996aa2f2d9a Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 10 Dec 2018 14:24:57 -0500 Subject: [PATCH 10/27] Merge PR #3070: Check for gas overflow in tx validation * Check for gas overflow in tx validation * Use bitshifting over math.Pow --- PENDING.md | 5 +++-- x/auth/stdtx.go | 9 ++++++++- x/auth/stdtx_test.go | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/PENDING.md b/PENDING.md index 95ecba5b5..932b3554a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -48,10 +48,11 @@ IMPROVEMENTS * Gaia * SDK - - \#1277 Complete bank module specification - - \#2963 Complete auth module specification + * \#1277 Complete bank module specification + * \#2963 Complete auth module specification * \#2914 No longer withdraw validator rewards on bond/unbond, but rather move the rewards to the respective validator's pools. + * \#3068 check for uint64 gas overflow during `Std#ValidateBasic`. * Tendermint diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index 59edf7c13..f648d7e77 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -10,7 +10,11 @@ import ( "github.com/tendermint/tendermint/crypto/multisig" ) -var _ sdk.Tx = (*StdTx)(nil) +var ( + _ sdk.Tx = (*StdTx)(nil) + + maxGasWanted = uint64((1 << 63) - 1) +) // StdTx is a standard way to wrap a Msg with Fee and Signatures. // NOTE: the first signature is the fee payer (Signatures must not be nil). @@ -38,6 +42,9 @@ func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } func (tx StdTx) ValidateBasic() sdk.Error { stdSigs := tx.GetSignatures() + if tx.Fee.Gas > maxGasWanted { + return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied; %d > %d", tx.Fee.Gas, maxGasWanted)) + } if !tx.Fee.Amount.IsNotNegative() { return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee %s amount provided", tx.Fee.Amount)) } diff --git a/x/auth/stdtx_test.go b/x/auth/stdtx_test.go index 1f2fefaca..735944420 100644 --- a/x/auth/stdtx_test.go +++ b/x/auth/stdtx_test.go @@ -120,6 +120,15 @@ func TestTxValidateBasic(t *testing.T) { require.Error(t, err) require.Equal(t, sdk.CodeTooManySignatures, err.Result().Code) + // require to fail with invalid gas supplied + badFee = newStdFee() + badFee.Gas = 9223372036854775808 + tx = newTestTx(ctx, nil, nil, nil, nil, badFee) + + err = tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeInternal, err.Result().Code) + // require to pass when above criteria are matched privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) From 243576143e3c72e6c6d60699323a341f549b5ad7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 10 Dec 2018 21:39:29 +0100 Subject: [PATCH 11/27] Merge PR #3072: Catch overflows in gas wanted * Check for overflow * Only expect block gas meter on DeliverTx * ErrGasOverflow in tx.ValidateBasic() * Update unit test --- PENDING.md | 1 + baseapp/baseapp.go | 8 ++++++++ types/errors.go | 4 ++++ x/auth/stdtx.go | 2 +- x/auth/stdtx_test.go | 2 +- 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index 932b3554a..3f5c8e967 100644 --- a/PENDING.md +++ b/PENDING.md @@ -69,5 +69,6 @@ BUG FIXES * SDK * \#2967 Change ordering of `mint.BeginBlocker` and `distr.BeginBlocker`, recalculate inflation each block + * \#3071 Catch overflow on block gas meter * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index eeeea53da..c3042b588 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -701,6 +701,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return } + var startingGas uint64 + if mode == runTxModeDeliver { + startingGas = ctx.BlockGasMeter().GasConsumed() + } + defer func() { if r := recover(); r != nil { switch rType := r.(type) { @@ -726,6 +731,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk if mode == runTxModeDeliver { ctx.BlockGasMeter().ConsumeGas( ctx.GasMeter().GasConsumedToLimit(), "block gas meter") + if ctx.BlockGasMeter().GasConsumed() < startingGas { + panic(sdk.ErrorGasOverflow{"tx gas summation"}) + } } }() diff --git a/types/errors.go b/types/errors.go index 46436531e..9e4d880df 100644 --- a/types/errors.go +++ b/types/errors.go @@ -43,6 +43,7 @@ const ( CodeMemoTooLarge CodeType = 13 CodeInsufficientFee CodeType = 14 CodeTooManySignatures CodeType = 15 + CodeGasOverflow CodeType = 16 // CodespaceRoot is a codespace for error codes in this file only. // Notice that 0 is an "unset" codespace, which can be overridden with @@ -143,6 +144,9 @@ func ErrInsufficientFee(msg string) Error { func ErrTooManySignatures(msg string) Error { return newErrorWithRootCodespace(CodeTooManySignatures, msg) } +func ErrGasOverflow(msg string) Error { + return newErrorWithRootCodespace(CodeGasOverflow, msg) +} //---------------------------------------- // Error & sdkError diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index f648d7e77..d1895a194 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -43,7 +43,7 @@ func (tx StdTx) ValidateBasic() sdk.Error { stdSigs := tx.GetSignatures() if tx.Fee.Gas > maxGasWanted { - return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied; %d > %d", tx.Fee.Gas, maxGasWanted)) + return sdk.ErrGasOverflow(fmt.Sprintf("invalid gas supplied; %d > %d", tx.Fee.Gas, maxGasWanted)) } if !tx.Fee.Amount.IsNotNegative() { return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee %s amount provided", tx.Fee.Amount)) diff --git a/x/auth/stdtx_test.go b/x/auth/stdtx_test.go index 735944420..1ad9ef6e2 100644 --- a/x/auth/stdtx_test.go +++ b/x/auth/stdtx_test.go @@ -127,7 +127,7 @@ func TestTxValidateBasic(t *testing.T) { err = tx.ValidateBasic() require.Error(t, err) - require.Equal(t, sdk.CodeInternal, err.Result().Code) + require.Equal(t, sdk.CodeGasOverflow, err.Result().Code) // require to pass when above criteria are matched privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} From c7646d2caf49d607d90570081dd72cb27d1dcba4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 10 Dec 2018 22:48:01 +0100 Subject: [PATCH 12/27] Release v0.28.0 back to develop (#3043) * PENDING.md => CHANGELOG.md --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ PENDING.md | 22 --------------------- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7894748a4..2ff20a938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,60 @@ # Changelog +## 0.28.0 + +BREAKING CHANGES + +* Gaia CLI (`gaiacli`) + * [cli] [\#2595](https://github.com/cosmos/cosmos-sdk/issues/2595) Remove `keys new` in favor of `keys add` incorporating existing functionality with addition of key recovery functionality. + * [cli] [\#2987](https://github.com/cosmos/cosmos-sdk/pull/2987) Add shorthand `-a` to `gaiacli keys show` and update docs + * [cli] [\#2971](https://github.com/cosmos/cosmos-sdk/pull/2971) Additional verification when running `gaiad gentx` + * [cli] [\#2734](https://github.com/cosmos/cosmos-sdk/issues/2734) Rewrite `gaiacli config`. It is now a non-interactive config utility. + +* Gaia + * [#128](https://github.com/tendermint/devops/issues/128) Updated CircleCI job to trigger website build on every push to master/develop. + * [\#2994](https://github.com/cosmos/cosmos-sdk/pull/2994) Change wrong-password error message. + * [\#3009](https://github.com/cosmos/cosmos-sdk/issues/3009) Added missing Gaia genesis verification + * [#128](https://github.com/tendermint/devops/issues/128) Updated CircleCI job to trigger website build on every push to master/develop. + * [\#2994](https://github.com/cosmos/cosmos-sdk/pull/2994) Change wrong-password error message. + * [\#3009](https://github.com/cosmos/cosmos-sdk/issues/3009) Added missing Gaia genesis verification + * [gas] [\#3052](https://github.com/cosmos/cosmos-sdk/issues/3052) Updated gas costs to more reasonable numbers + +* SDK + * [auth] [\#2952](https://github.com/cosmos/cosmos-sdk/issues/2952) Signatures are no longer serialized on chain with the account number and sequence number + * [auth] [\#2952](https://github.com/cosmos/cosmos-sdk/issues/2952) Signatures are no longer serialized on chain with the account number and sequence number + * [stake] [\#3055](https://github.com/cosmos/cosmos-sdk/issues/3055) Use address instead of bond height / intratxcounter for deduplication + +FEATURES + +* Gaia CLI (`gaiacli`) + * [\#2961](https://github.com/cosmos/cosmos-sdk/issues/2961) Add --force flag to gaiacli keys delete command to skip passphrase check and force key deletion unconditionally. + +IMPROVEMENTS + +* Gaia CLI (`gaiacli`) + * [\#2991](https://github.com/cosmos/cosmos-sdk/issues/2991) Fully validate transaction signatures during `gaiacli tx sign --validate-signatures` + +* SDK + * [\#1277](https://github.com/cosmos/cosmos-sdk/issues/1277) Complete bank module specification + * [\#2963](https://github.com/cosmos/cosmos-sdk/issues/2963) Complete auth module specification + * [\#2914](https://github.com/cosmos/cosmos-sdk/issues/2914) No longer withdraw validator rewards on bond/unbond, but rather move + the rewards to the respective validator's pools. + + +BUG FIXES + +* Gaia CLI (`gaiacli`) + * [\#2921](https://github.com/cosmos/cosmos-sdk/issues/2921) Fix `keys delete` inability to delete offline and ledger keys. + +* Gaia + * [\#3003](https://github.com/cosmos/cosmos-sdk/issues/3003) CollectStdTxs() must validate DelegatorAddr against genesis accounts. + +* SDK + * [\#2967](https://github.com/cosmos/cosmos-sdk/issues/2967) Change ordering of `mint.BeginBlocker` and `distr.BeginBlocker`, recalculate inflation each block + * [\#3068](https://github.com/cosmos/cosmos-sdk/issues/3068) check for uint64 gas overflow during `Std#ValidateBasic`. + * [\#3071](https://github.com/cosmos/cosmos-sdk/issues/3071) Catch overflow on block gas meter + + ## 0.27.0 BREAKING CHANGES diff --git a/PENDING.md b/PENDING.md index 3f5c8e967..4d87301f0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -5,20 +5,10 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [cli] [\#2595](https://github.com/cosmos/cosmos-sdk/issues/2595) Remove `keys new` in favor of `keys add` incorporating existing functionality with addition of key recovery functionality. - * [cli] [\#2987](https://github.com/cosmos/cosmos-sdk/pull/2987) Add shorthand `-a` to `gaiacli keys show` and update docs - * [cli] [\#2971](https://github.com/cosmos/cosmos-sdk/pull/2971) Additional verification when running `gaiad gentx` - * [cli] [\#2734](https://github.com/cosmos/cosmos-sdk/issues/2734) Rewrite `gaiacli config`. It is now a non-interactive config utility. * Gaia - - [#128](https://github.com/tendermint/devops/issues/128) Updated CircleCI job to trigger website build on every push to master/develop. - - [\#2994](https://github.com/cosmos/cosmos-sdk/pull/2994) Change wrong-password error message. - - \#3009 Added missing Gaia genesis verification - - [gas] \#3052 Updated gas costs to more reasonable numbers * SDK - - [auth] \#2952 Signatures are no longer serialized on chain with the account number and sequence number - - [stake] \#3055 Use address instead of bond height / intratxcounter for deduplication * Tendermint @@ -28,10 +18,8 @@ FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - - [\#2961](https://github.com/cosmos/cosmos-sdk/issues/2961) Add --force flag to gaiacli keys delete command to skip passphrase check and force key deletion unconditionally. * Gaia - - [gov] Added minimum quorum needed for vote to pass * SDK @@ -43,16 +31,10 @@ IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * \#2991 Fully validate transaction signatures during `gaiacli tx sign --validate-signatures` * Gaia * SDK - * \#1277 Complete bank module specification - * \#2963 Complete auth module specification - * \#2914 No longer withdraw validator rewards on bond/unbond, but rather move - the rewards to the respective validator's pools. - * \#3068 check for uint64 gas overflow during `Std#ValidateBasic`. * Tendermint @@ -62,13 +44,9 @@ BUG FIXES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) - * [\#2921](https://github.com/cosmos/cosmos-sdk/issues/2921) Fix `keys delete` inability to delete offline and ledger keys. * Gaia - * [\#3003](https://github.com/cosmos/cosmos-sdk/issues/3003) CollectStdTxs() must validate DelegatorAddr against genesis accounts. * SDK - * \#2967 Change ordering of `mint.BeginBlocker` and `distr.BeginBlocker`, recalculate inflation each block - * \#3071 Catch overflow on block gas meter * Tendermint From 5c3bef97fb17ee06f4da16be297dea00c0d40ecf Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 10 Dec 2018 18:22:08 -0800 Subject: [PATCH 13/27] Code cleanup and refactor of LCD tests --- client/lcd/lcd_test.go | 872 +++---------------------------------- client/lcd/test_helpers.go | 857 +++++++++++++++++++++++++++++++++++- 2 files changed, 918 insertions(+), 811 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 34f8edbc9..0c88d45c6 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -7,7 +7,6 @@ import ( "net/http" "os" "regexp" - "strings" "testing" "time" @@ -18,110 +17,79 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" client "github.com/cosmos/cosmos-sdk/client" - keys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" - cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" tests "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" - version "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" ) +const ( + name1 = "test1" + name2 = "test2" + name3 = "test3" + pw = app.DefaultKeyPass + altPw = "12345678901" +) + func init() { mintkey.BcryptSecurityParameter = 1 version.Version = os.Getenv("VERSION") } func TestKeys(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() - // get seed - // TODO Do we really need this endpoint? - res, body := Request(t, port, "GET", "/keys/seed", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - reg, err := regexp.Compile(`([a-z]+ ){12}`) - require.Nil(t, err) - match := reg.MatchString(seed) - require.True(t, match, "Returned seed has wrong format", seed) + // get new seed + seed := getKeysSeed(t, port) // recover key - recoverName := "test_recovername" - recoverPassword := "1234567890" - doRecoverKey(t, port, recoverName, recoverPassword, seed) + doRecoverKey(t, port, name2, pw, seed) - newName := "test_newname" - newPassword := "0987654321" // add key - jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed)) - res, body = Request(t, port, "POST", "/keys", jsonStr) - - require.Equal(t, http.StatusOK, res.StatusCode, body) - var resp keys.KeyOutput - err = codec.Cdc.UnmarshalJSON([]byte(body), &resp) - require.Nil(t, err, body) + resp := doKeysPost(t, port, name3, pw, seed) + addrBech32 := addr.String() addr2Bech32 := resp.Address - _, err = sdk.AccAddressFromBech32(addr2Bech32) + _, err := sdk.AccAddressFromBech32(addr2Bech32) require.NoError(t, err, "Failed to return a correct bech32 address") // test if created account is the correct account - expectedInfo, _ := GetKeyBase(t).CreateKey(newName, seed, newPassword) + expectedInfo, _ := GetKeyBase(t).CreateKey(name3, seed, pw) expectedAccount := sdk.AccAddress(expectedInfo.GetPubKey().Address().Bytes()) require.Equal(t, expectedAccount.String(), addr2Bech32) // existing keys - res, body = Request(t, port, "GET", "/keys", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var m [3]keys.KeyOutput - err = cdc.UnmarshalJSON([]byte(body), &m) - require.Nil(t, err) - - addrBech32 := addr.String() - - require.Equal(t, name, m[0].Name, "Did not serve keys name correctly") - require.Equal(t, addrBech32, m[0].Address, "Did not serve keys Address correctly") - require.Equal(t, newName, m[1].Name, "Did not serve keys name correctly") - require.Equal(t, addr2Bech32, m[1].Address, "Did not serve keys Address correctly") + keys := getKeys(t, port) + require.Equal(t, name1, keys[0].Name, "Did not serve keys name correctly") + require.Equal(t, addrBech32, keys[0].Address, "Did not serve keys Address correctly") + require.Equal(t, name2, keys[1].Name, "Did not serve keys name correctly") + require.Equal(t, addr2Bech32, keys[1].Address, "Did not serve keys Address correctly") // select key - keyEndpoint := fmt.Sprintf("/keys/%s", newName) - 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) - - require.Equal(t, newName, m2.Name, "Did not serve keys name correctly") - require.Equal(t, addr2Bech32, m2.Address, "Did not serve keys Address correctly") + key := getKey(t, port, name3) + require.Equal(t, name3, key.Name, "Did not serve keys name correctly") + require.Equal(t, addr2Bech32, key.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) - require.Equal(t, http.StatusOK, res.StatusCode, body) + updateKey(t, port, name3, pw, altPw, false) // here it should say unauthorized as we changed the password before - res, body = Request(t, port, "PUT", keyEndpoint, jsonStr) - require.Equal(t, http.StatusUnauthorized, res.StatusCode, body) + updateKey(t, port, name3, pw, altPw, true) // delete key - jsonStr = []byte(`{"password":"12345678901"}`) - res, body = Request(t, port, "DELETE", keyEndpoint, jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) + deleteKey(t, port, name3, altPw) } func TestVersion(t *testing.T) { @@ -238,8 +206,7 @@ func TestValidators(t *testing.T) { } func TestCoinSend(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() @@ -255,7 +222,7 @@ func TestCoinSend(t *testing.T) { initialBalance := acc.GetCoins() // create TX - receiveAddr, resultTx := doSend(t, port, seed, name, password, addr) + receiveAddr, resultTx := doSend(t, port, seed, name1, pw, addr) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -279,70 +246,40 @@ func TestCoinSend(t *testing.T) { require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, false, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "100", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with negative gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, false, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "-200", 0, false, false) require.Equal(t, http.StatusBadRequest, res.StatusCode, body) // test failure with 0 gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, false, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "0", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with wrong adjustment - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 0.1, false, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "simulate", 0.1, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 0, true, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "", 0, true, false) require.Equal(t, http.StatusOK, res.StatusCode, body) var responseBody struct { GasEstimate int64 `json:"gas_estimate"` } require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, false, false) + res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, false, false) require.Equal(t, http.StatusOK, res.StatusCode, body) } -func DisabledTestIBCTransfer(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) - defer cleanup() - - acc := getAccount(t, port, addr) - initialBalance := acc.GetCoins() - - // create TX - resultTx := doIBCTransfer(t, port, seed, name, password, addr) - - tests.WaitForHeight(resultTx.Height+1, port) - - // check if tx was committed - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - - // query sender - acc = getAccount(t, port, addr) - coins := acc.GetCoins() - mycoins := coins[0] - - require.Equal(t, stakeTypes.DefaultBondDenom, mycoins.Denom) - require.Equal(t, initialBalance[0].Amount.SubRaw(1), mycoins.Amount) - - // TODO: query ibc egress packet state -} - func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, false, true) + res, body, _ := doSendWithGas(t, port, seed, name1, pw, addr, "simulate", 0, false, true) require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -359,8 +296,8 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { payload := authrest.SignBody{ Tx: msg, - LocalAccountName: name, - Password: password, + LocalAccountName: name1, + Password: pw, ChainID: viper.GetString(client.FlagChainID), AccountNumber: accnum, Sequence: sequence, @@ -395,8 +332,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { } func TestTxs(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() @@ -416,7 +352,7 @@ func TestTxs(t *testing.T) { require.Equal(t, emptyTxs, txs) // create tx - receiveAddr, resultTx := doSend(t, port, seed, name, password, addr) + receiveAddr, resultTx := doSend(t, port, seed, name1, pw, addr) tests.WaitForHeight(resultTx.Height+1, port) // check if tx is queryable @@ -436,8 +372,7 @@ func TestTxs(t *testing.T) { } func TestPoolParamsQuery(t *testing.T) { - _, password := "test", "1234567890" - addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() @@ -498,8 +433,7 @@ func TestValidatorQuery(t *testing.T) { } func TestBonding(t *testing.T) { - name, password, denom := "test", "1234567890", stakeTypes.DefaultBondDenom - addr, seed := CreateAddr(t, name, password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr}) defer cleanup() @@ -511,7 +445,7 @@ func TestBonding(t *testing.T) { validator := getValidator(t, port, operAddrs[0]) // create bond TX - resultTx := doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60) + resultTx := doDelegate(t, port, seed, name1, pw, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -528,7 +462,7 @@ func TestBonding(t *testing.T) { acc := getAccount(t, port, addr) coins := acc.GetCoins() - require.Equal(t, int64(40), coins.AmountOf(denom).Int64()) + require.Equal(t, int64(40), coins.AmountOf(stakeTypes.DefaultBondDenom).Int64()) // query delegation bond := getDelegation(t, port, addr, operAddrs[0]) @@ -551,7 +485,7 @@ func TestBonding(t *testing.T) { require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr) // testing unbonding - resultTx = doBeginUnbonding(t, port, seed, name, password, addr, operAddrs[0], 30) + resultTx = doBeginUnbonding(t, port, seed, name1, pw, addr, operAddrs[0], 30) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -574,7 +508,7 @@ func TestBonding(t *testing.T) { require.Equal(t, "30", unbonding.Balance.Amount.String()) // test redelegation - resultTx = doBeginRedelegation(t, port, seed, name, password, addr, operAddrs[0], operAddrs[1], 30) + resultTx = doBeginRedelegation(t, port, seed, name1, pw, addr, operAddrs[0], operAddrs[1], 30) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -628,13 +562,12 @@ func TestBonding(t *testing.T) { } func TestSubmitProposal(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name, password, addr, 5) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 5) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -655,13 +588,12 @@ func TestSubmitProposal(t *testing.T) { } func TestDeposit(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name, password, addr, 5) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 5) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -676,7 +608,7 @@ func TestDeposit(t *testing.T) { require.Equal(t, "Test", proposal.GetTitle()) // create SubmitProposal TX - resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5) + resultTx = doDeposit(t, port, seed, name1, pw, addr, proposalID, 5) tests.WaitForHeight(resultTx.Height+1, port) // query tx @@ -694,13 +626,12 @@ func TestDeposit(t *testing.T) { } func TestVote(t *testing.T) { - name, password := "test", "1234567890" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name, password, addr, 5) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 5) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -715,7 +646,7 @@ func TestVote(t *testing.T) { require.Equal(t, "Test", proposal.GetTitle()) // deposit - resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5) + resultTx = doDeposit(t, port, seed, name1, pw, addr, proposalID, 5) tests.WaitForHeight(resultTx.Height+1, port) // query proposal @@ -723,7 +654,7 @@ func TestVote(t *testing.T) { require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus()) // vote - resultTx = doVote(t, port, seed, name, password, addr, proposalID) + resultTx = doVote(t, port, seed, name1, pw, addr, proposalID) tests.WaitForHeight(resultTx.Height+1, port) // query tx @@ -739,11 +670,11 @@ func TestVote(t *testing.T) { require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 as the address is not bonded") // create bond TX - resultTx = doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60) + resultTx = doDelegate(t, port, seed, name1, pw, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) // vote - resultTx = doVote(t, port, seed, name, password, addr, proposalID) + resultTx = doVote(t, port, seed, name1, pw, addr, proposalID) tests.WaitForHeight(resultTx.Height+1, port) tally = getTally(t, port, proposalID) @@ -751,8 +682,7 @@ func TestVote(t *testing.T) { } func TestUnjail(t *testing.T) { - _, password := "test", "1234567890" - addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() @@ -885,681 +815,3 @@ func TestProposalsQuery(t *testing.T) { require.True(t, addrs[0].String() == votes[0].Voter.String() || addrs[0].String() == votes[1].Voter.String()) require.True(t, addrs[1].String() == votes[0].Voter.String() || addrs[1].String() == votes[1].Voter.String()) } - -//_____________________________________________________________________________ -// get the account to get the sequence -func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { - res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr.String()), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var acc auth.Account - err := cdc.UnmarshalJSON([]byte(body), &acc) - require.Nil(t, err) - return acc -} - -func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, - gasAdjustment float64, simulate, generateOnly bool) ( - res *http.Response, body string, receiveAddr sdk.AccAddress) { - - // create receive address - kb := client.MockKeyBase() - receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1")) - require.Nil(t, err) - receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) - - acc := getAccount(t, port, addr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - chainID := viper.GetString(client.FlagChainID) - // send - coinbz, err := cdc.MarshalJSON(sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1)) - if err != nil { - panic(err) - } - - gasStr := "" - if len(gas) != 0 { - gasStr = fmt.Sprintf(` - "gas":%q, - `, gas) - } - gasAdjustmentStr := "" - if gasAdjustment > 0 { - gasAdjustmentStr = fmt.Sprintf(` - "gas_adjustment":"%v", - `, gasAdjustment) - } - jsonStr := []byte(fmt.Sprintf(`{ - "amount":[%s], - "base_req": { - %v%v - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence": "%d", - "simulate": %v, - "generate_only": %v - } - }`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence, simulate, generateOnly)) - - res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), jsonStr) - return -} - -func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) { - jsonStr := []byte(fmt.Sprintf(`{"password":"%s", "seed":"%s"}`, recoverPassword, seed)) - res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), jsonStr) - - require.Equal(t, http.StatusOK, res.StatusCode, body) - var resp keys.KeyOutput - err := codec.Cdc.UnmarshalJSON([]byte(body), &resp) - require.Nil(t, err, body) - - addr1Bech32 := resp.Address - _, err = sdk.AccAddressFromBech32(addr1Bech32) - require.NoError(t, err, "Failed to return a correct bech32 address") -} - -func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { - res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, false, false) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err := cdc.UnmarshalJSON([]byte(body), &resultTx) - require.Nil(t, err) - - return receiveAddr, resultTx -} - -func getTransactions(t *testing.T, port string, tags ...string) []tx.Info { - var txs []tx.Info - if len(tags) == 0 { - return txs - } - queryStr := strings.Join(tags, "&") - res, body := Request(t, port, "GET", fmt.Sprintf("/txs?%s", queryStr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err := cdc.UnmarshalJSON([]byte(body), &txs) - require.NoError(t, err) - return txs -} - -// ============= IBC Module ================ - -func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { - // create receive address - kb := client.MockKeyBase() - receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1")) - require.Nil(t, err) - receiveAddr := sdk.AccAddress(receiveInfo.GetPubKey().Address()) - - chainID := viper.GetString(client.FlagChainID) - - // get the account to get the sequence - acc := getAccount(t, port, addr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - - // send - jsonStr := []byte(fmt.Sprintf(`{ - "amount":[ - { - "denom": "%s", - "amount": "1" - } - ], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence":"%d" - } - }`, stakeTypes.DefaultBondDenom, name, password, chainID, accnum, sequence)) - - res, body := Request(t, port, "POST", fmt.Sprintf("/ibc/testchain/%s/send", receiveAddr), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err = cdc.UnmarshalJSON([]byte(body), &resultTx) - require.Nil(t, err) - - return resultTx -} - -// ============= Slashing Module ================ - -func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { - res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/validators/%s/signing_info", validatorPubKey), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var signingInfo slashing.ValidatorSigningInfo - err := cdc.UnmarshalJSON([]byte(body), &signingInfo) - require.Nil(t, err) - - return signingInfo -} - -func doUnjail(t *testing.T, port, seed, name, password string, - valAddr sdk.ValAddress) (resultTx ctypes.ResultBroadcastTxCommit) { - chainID := viper.GetString(client.FlagChainID) - - jsonStr := []byte(fmt.Sprintf(`{ - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"1", - "sequence":"1" - } - }`, name, password, chainID)) - - res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), jsonStr) - // TODO : fails with "401 must use own validator address" - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results[0] -} - -// ============= Stake Module ================ - -func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var bond stake.Delegation - err := cdc.UnmarshalJSON([]byte(body), &bond) - require.Nil(t, err) - - return bond -} - -func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.UnbondingDelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var unbond stake.UnbondingDelegation - err := cdc.UnmarshalJSON([]byte(body), &unbond) - require.Nil(t, err) - - return unbond -} - -func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Delegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var dels []stake.Delegation - - err := cdc.UnmarshalJSON([]byte(body), &dels) - require.Nil(t, err) - - return dels -} - -func getDelegatorUnbondingDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.UnbondingDelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations", delegatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var ubds []stake.UnbondingDelegation - - err := cdc.UnmarshalJSON([]byte(body), &ubds) - require.Nil(t, err) - - return ubds -} - -func getDelegatorRedelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Redelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/redelegations", delegatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var reds []stake.Redelegation - - err := cdc.UnmarshalJSON([]byte(body), &reds) - require.Nil(t, err) - - return reds -} - -func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { - var res *http.Response - var body string - - if len(query) > 0 { - res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil) - } else { - res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil) - } - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var txs []tx.Info - - err := cdc.UnmarshalJSON([]byte(body), &txs) - require.Nil(t, err) - - return txs -} - -func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Validator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators", delegatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var bondedValidators []stake.Validator - - err := cdc.UnmarshalJSON([]byte(body), &bondedValidators) - require.Nil(t, err) - - return bondedValidators -} - -func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Validator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var bondedValidator stake.Validator - err := cdc.UnmarshalJSON([]byte(body), &bondedValidator) - require.Nil(t, err) - - return bondedValidator -} - -func doDelegate(t *testing.T, port, seed, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - - acc := getAccount(t, port, delAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - chainID := viper.GetString(client.FlagChainID) - - jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "delegation": { "denom": "%s", "amount": "%d" } - } - ], - "begin_unbondings": [], - "begin_redelegates": [], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence":"%d" - } - }`, delAddr, valAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) - - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results[0] -} - -func doBeginUnbonding(t *testing.T, port, seed, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - - acc := getAccount(t, port, delAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - chainID := viper.GetString(client.FlagChainID) - - jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "shares": "%d" - } - ], - "begin_redelegates": [], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence":"%d" - } - }`, delAddr, valAddr, amount, name, password, chainID, accnum, sequence)) - - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results[0] -} - -func doBeginRedelegation(t *testing.T, port, seed, name, password string, - delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - - acc := getAccount(t, port, delAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - - chainID := viper.GetString(client.FlagChainID) - - jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [], - "begin_redelegates": [ - { - "delegator_addr": "%s", - "validator_src_addr": "%s", - "validator_dst_addr": "%s", - "shares": "%d" - } - ], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence":"%d" - } - }`, delAddr, valSrcAddr, valDstAddr, amount, name, password, chainID, accnum, sequence)) - - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results[0] -} - -func getValidators(t *testing.T, port string) []stake.Validator { - res, body := Request(t, port, "GET", "/stake/validators", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var validators []stake.Validator - err := cdc.UnmarshalJSON([]byte(body), &validators) - require.Nil(t, err) - - return validators -} - -func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake.Validator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var validator stake.Validator - err := cdc.UnmarshalJSON([]byte(body), &validator) - require.Nil(t, err) - - return validator -} - -func getValidatorDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Delegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/delegations", validatorAddr.String()), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var delegations []stake.Delegation - err := cdc.UnmarshalJSON([]byte(body), &delegations) - require.Nil(t, err) - - return delegations -} - -func getValidatorUnbondingDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.UnbondingDelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/unbonding_delegations", validatorAddr.String()), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var ubds []stake.UnbondingDelegation - err := cdc.UnmarshalJSON([]byte(body), &ubds) - require.Nil(t, err) - - return ubds -} - -func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Redelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/redelegations", validatorAddr.String()), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var reds []stake.Redelegation - err := cdc.UnmarshalJSON([]byte(body), &reds) - require.Nil(t, err) - - return reds -} - -// ============= Governance Module ================ - -func getDepositParam(t *testing.T, port string) gov.DepositParams { - res, body := Request(t, port, "GET", "/gov/parameters/deposit", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var depositParams gov.DepositParams - err := cdc.UnmarshalJSON([]byte(body), &depositParams) - require.Nil(t, err) - return depositParams -} - -func getVotingParam(t *testing.T, port string) gov.VotingParams { - res, body := Request(t, port, "GET", "/gov/parameters/voting", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var votingParams gov.VotingParams - err := cdc.UnmarshalJSON([]byte(body), &votingParams) - require.Nil(t, err) - return votingParams -} - -func getTallyingParam(t *testing.T, port string) gov.TallyParams { - res, body := Request(t, port, "GET", "/gov/parameters/tallying", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var tallyParams gov.TallyParams - err := cdc.UnmarshalJSON([]byte(body), &tallyParams) - require.Nil(t, err) - return tallyParams -} - -func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var proposal gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposal) - require.Nil(t, err) - return proposal -} - -func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var deposits []gov.Deposit - err := cdc.UnmarshalJSON([]byte(body), &deposits) - require.Nil(t, err) - return deposits -} - -func getDeposit(t *testing.T, port string, proposalID uint64, depositorAddr sdk.AccAddress) gov.Deposit { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var deposit gov.Deposit - err := cdc.UnmarshalJSON([]byte(body), &deposit) - require.Nil(t, err) - return deposit -} - -func getVote(t *testing.T, port string, proposalID uint64, voterAddr sdk.AccAddress) gov.Vote { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var vote gov.Vote - err := cdc.UnmarshalJSON([]byte(body), &vote) - require.Nil(t, err) - return vote -} - -func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var votes []gov.Vote - err := cdc.UnmarshalJSON([]byte(body), &votes) - require.Nil(t, err) - return votes -} - -func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/tally", proposalID), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - var tally gov.TallyResult - err := cdc.UnmarshalJSON([]byte(body), &tally) - require.Nil(t, err) - return tally -} - -func getProposalsAll(t *testing.T, port string) []gov.Proposal { - res, body := Request(t, port, "GET", "/gov/proposals", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var proposals []gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposals) - require.Nil(t, err) - return proposals -} - -func getProposalsFilterDepositor(t *testing.T, port string, depositorAddr sdk.AccAddress) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s", depositorAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var proposals []gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposals) - require.Nil(t, err) - return proposals -} - -func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?voter=%s", voterAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var proposals []gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposals) - require.Nil(t, err) - return proposals -} - -func getProposalsFilterVoterDepositor(t *testing.T, port string, voterAddr, depositorAddr sdk.AccAddress) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s&voter=%s", depositorAddr, voterAddr), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var proposals []gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposals) - require.Nil(t, err) - return proposals -} - -func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStatus) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?status=%s", status), nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var proposals []gov.Proposal - err := cdc.UnmarshalJSON([]byte(body), &proposals) - require.Nil(t, err) - return proposals -} - -func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - - acc := getAccount(t, port, proposerAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - - chainID := viper.GetString(client.FlagChainID) - - // submitproposal - jsonStr := []byte(fmt.Sprintf(`{ - "title": "Test", - "description": "test", - "proposal_type": "Text", - "proposer": "%s", - "initial_deposit": [{ "denom": "%s", "amount": "%d" }], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence":"%d" - } - }`, proposerAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", "/gov/proposals", jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results -} - -func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - - acc := getAccount(t, port, proposerAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - - chainID := viper.GetString(client.FlagChainID) - - // deposit on proposal - jsonStr := []byte(fmt.Sprintf(`{ - "depositor": "%s", - "amount": [{ "denom": "%s", "amount": "%d" }], - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number":"%d", - "sequence": "%d" - } - }`, proposerAddr, stakeTypes.DefaultBondDenom, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results -} - -func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence - acc := getAccount(t, port, proposerAddr) - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() - - chainID := viper.GetString(client.FlagChainID) - - // vote on proposal - jsonStr := []byte(fmt.Sprintf(`{ - "voter": "%s", - "option": "Yes", - "base_req": { - "name": "%s", - "password": "%s", - "chain_id": "%s", - "account_number": "%d", - "sequence": "%d" - } - }`, proposerAddr, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) - require.Nil(t, err) - - return results -} diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 87f57f385..d716232c6 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "regexp" "io/ioutil" "net" @@ -16,12 +17,17 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" + cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/slashing" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" + ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/client/utils" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -198,6 +204,7 @@ func (b AddrSeedSlice) Swap(i, j int) { b[j], b[i] = b[i], b[j] } +// TODO: Make InitializeTestLCD safe to call in multiple tests at the same time // InitializeTestLCD starts Tendermint and the LCD in process, listening on // their respective sockets where nValidators is the total number of validators // and initAddrs are the accounts to initialize with some steak tokens. It @@ -394,7 +401,7 @@ func Request(t *testing.T, port, method, path string, payload []byte) (*http.Res res *http.Response ) url := fmt.Sprintf("http://localhost:%v%v", port, path) - fmt.Println("REQUEST " + method + " " + url) + fmt.Printf("REQUEST %s %s\n", method, url) req, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) require.Nil(t, err) @@ -408,3 +415,851 @@ func Request(t *testing.T, port, method, path string, payload []byte) (*http.Res return res, string(output) } + +// ---------------------------------------------------------------------- +// ICS 0 - Tendermint +// ---------------------------------------------------------------------- +// GET /node_info The properties of the connected node +// SEE TestNodeStatus + +// GET /syncing Syncing state of node +// SEE TestNodeStatus + +// GET /blocks/latest Get the latest block +// SEE TestBlock + +// GET /blocks/{height} Get a block at a certain height +// SEE TestBlock + +// GET /validatorsets/latest Get the latest validator set +// SEE TestValidators + +// GET /validatorsets/{height} Get a validator set a certain height +// SEE TestValidators + +// GET /txs/{hash} get tx by hash +// POST /txs broadcast txs + +// GET /txs search transactions +func getTransactions(t *testing.T, port string, tags ...string) []tx.Info { + var txs []tx.Info + if len(tags) == 0 { + return txs + } + queryStr := strings.Join(tags, "&") + res, body := Request(t, port, "GET", fmt.Sprintf("/txs?%s", queryStr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &txs) + require.NoError(t, err) + return txs +} + +// ---------------------------------------------------------------------- +// ICS 1 - Keys +// ---------------------------------------------------------------------- +// GET /keys List of accounts stored locally +func getKeys(t *testing.T, port string) []keys.KeyOutput { + res, body := Request(t, port, "GET", "/keys", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var m []keys.KeyOutput + err := cdc.UnmarshalJSON([]byte(body), &m) + require.Nil(t, err) + return m +} + +// POST /keys Create a new account locally +func doKeysPost(t *testing.T, port, name, password, seed string) keys.KeyOutput { + pk := postKeys{name, password, seed} + req, err := cdc.MarshalJSON(pk) + require.NoError(t, err) + res, body := Request(t, port, "POST", "/keys", req) + + require.Equal(t, http.StatusOK, res.StatusCode, body) + var resp keys.KeyOutput + err = cdc.UnmarshalJSON([]byte(body), &resp) + require.Nil(t, err, body) + return resp +} + +type postKeys struct { + Name string `json:"name"` + Password string `json:"password"` + Seed string `json:"seed"` +} + +// GET /keys/seed Create a new seed to create a new account defaultValidFor +func getKeysSeed(t *testing.T, port string) string { + res, body := Request(t, port, "GET", "/keys/seed", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + reg, err := regexp.Compile(`([a-z]+ ){12}`) + require.Nil(t, err) + match := reg.MatchString(body) + require.True(t, match, "Returned seed has wrong format", body) + return body +} + +// POST /keys/{name}/recover Recover a account from a seed +func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) { + jsonStr := []byte(fmt.Sprintf(`{"password":"%s", "seed":"%s"}`, recoverPassword, seed)) + res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), jsonStr) + + require.Equal(t, http.StatusOK, res.StatusCode, body) + var resp keys.KeyOutput + err := codec.Cdc.UnmarshalJSON([]byte(body), &resp) + require.Nil(t, err, body) + + addr1Bech32 := resp.Address + _, err = sdk.AccAddressFromBech32(addr1Bech32) + require.NoError(t, err, "Failed to return a correct bech32 address") +} + +// GET /keys/{name} Get a certain locally stored account +func getKey(t *testing.T, port, name string) keys.KeyOutput { + res, body := Request(t, port, "GET", fmt.Sprintf("/keys/%s", name), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var resp keys.KeyOutput + err := cdc.UnmarshalJSON([]byte(body), &resp) + require.Nil(t, err) + return resp +} + +// PUT /keys/{name} Update the password for this account in the KMS +func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) { + kr := updateKeyReq{oldPassword, newPassword} + req, err := cdc.MarshalJSON(kr) + require.NoError(t, err) + keyEndpoint := fmt.Sprintf("/keys/%s", name) + res, body := Request(t, port, "PUT", keyEndpoint, req) + if fail { + require.Equal(t, http.StatusUnauthorized, res.StatusCode, body) + return + } + require.Equal(t, http.StatusOK, res.StatusCode, body) +} + +type updateKeyReq struct { + OldPassword string `json:"old_password"` + NewPassword string `json:"new_password"` +} + +// DELETE /keys/{name} Remove an account +func deleteKey(t *testing.T, port, name, password string) { + dk := deleteKeyReq{password} + req, err := cdc.MarshalJSON(dk) + require.NoError(t, err) + keyEndpoint := fmt.Sprintf("/keys/%s", name) + res, body := Request(t, port, "DELETE", keyEndpoint, req) + require.Equal(t, http.StatusOK, res.StatusCode, body) +} + +type deleteKeyReq struct { + Password string `json:"password"` +} + +// GET /auth/accounts/{address} Get the account information on blockchain +func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { + res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var acc auth.Account + err := cdc.UnmarshalJSON([]byte(body), &acc) + require.Nil(t, err) + return acc +} + +// ---------------------------------------------------------------------- +// ICS 20 - Tokens +// ---------------------------------------------------------------------- + +// TODO: TEST THE FOLLOWING ROUTES +// POST /tx/sign Sign a Tx +// POST /tx/broadcast Send a signed Tx +// GET /bank/balances/{address} Get the account balances + +// POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send) +func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { + res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, false, false) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &resultTx) + require.Nil(t, err) + + return receiveAddr, resultTx +} + +func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, + gasAdjustment float64, simulate, generateOnly bool) ( + res *http.Response, body string, receiveAddr sdk.AccAddress) { + + // create receive address + kb := client.MockKeyBase() + receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1")) + require.Nil(t, err) + receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) + + acc := getAccount(t, port, addr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + + sr := sendReq{ + Amount: sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1)}, + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + Simulate: simulate, + GenerateOnly: generateOnly, + }, + } + + if len(gas) != 0 { + sr.BaseReq.Gas = gas + } + + if gasAdjustment > 0 { + sr.BaseReq.GasAdjustment = fmt.Sprintf("%f", gasAdjustment) + } + + req, err := cdc.MarshalJSON(sr) + require.NoError(t, err) + + res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req) + return +} + +type sendReq struct { + Amount sdk.Coins `json:"amount"` + BaseReq utils.BaseReq `json:"base_req"` +} + +// ---------------------------------------------------------------------- +// ICS 21 - Stake +// ---------------------------------------------------------------------- + +type editDelegationsReq struct { + BaseReq utils.BaseReq `json:"base_req"` + Delegations []msgDelegationsInput `json:"delegations"` + BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` + BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"` +} + +// POST /stake/delegators/{delegatorAddr}/delegations Submit delegation +func doDelegate(t *testing.T, port, seed, name, password string, + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { + + acc := getAccount(t, port, delAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + ed := editDelegationsReq{ + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + Delegations: []msgDelegationsInput{msgDelegationsInput{ + DelegatorAddr: sdk.AccAddress(delAddr).String(), + ValidatorAddr: sdk.ValAddress(valAddr).String(), + Delegation: sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, amount), + }}, + } + req, err := cdc.MarshalJSON(ed) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +type msgDelegationsInput struct { + DelegatorAddr string `json:"delegator_addr"` // in bech32 + ValidatorAddr string `json:"validator_addr"` // in bech32 + Delegation sdk.Coin `json:"delegation"` +} + +// POST /stake/delegators/{delegatorAddr}/delegations Submit delegation +func doBeginUnbonding(t *testing.T, port, seed, name, password string, + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { + + acc := getAccount(t, port, delAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + ed := editDelegationsReq{ + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + BeginUnbondings: []msgBeginUnbondingInput{msgBeginUnbondingInput{ + DelegatorAddr: sdk.AccAddress(delAddr).String(), + ValidatorAddr: sdk.ValAddress(valAddr).String(), + SharesAmount: fmt.Sprintf("%d", amount), + }}, + } + req, err := cdc.MarshalJSON(ed) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +type msgBeginUnbondingInput struct { + DelegatorAddr string `json:"delegator_addr"` // in bech32 + ValidatorAddr string `json:"validator_addr"` // in bech32 + SharesAmount string `json:"shares"` +} + +// POST /stake/delegators/{delegatorAddr}/delegations Submit delegation +func doBeginRedelegation(t *testing.T, port, seed, name, password string, + delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { + + acc := getAccount(t, port, delAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + ed := editDelegationsReq{ + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + BeginRedelegates: []msgBeginRedelegateInput{msgBeginRedelegateInput{ + DelegatorAddr: sdk.AccAddress(delAddr).String(), + ValidatorSrcAddr: sdk.ValAddress(valSrcAddr).String(), + ValidatorDstAddr: sdk.ValAddress(valDstAddr).String(), + SharesAmount: fmt.Sprintf("%d", amount), + }}, + } + req, err := cdc.MarshalJSON(ed) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +type msgBeginRedelegateInput struct { + DelegatorAddr string `json:"delegator_addr"` // in bech32 + ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32 + ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32 + SharesAmount string `json:"shares"` +} + +// GET /stake/delegators/{delegatorAddr}/delegations Get all delegations from a delegator +func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Delegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var dels []stake.Delegation + + err := cdc.UnmarshalJSON([]byte(body), &dels) + require.Nil(t, err) + + return dels +} + +// GET /stake/delegators/{delegatorAddr}/unbonding_delegations Get all unbonding delegations from a delegator +func getDelegatorUnbondingDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.UnbondingDelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var ubds []stake.UnbondingDelegation + + err := cdc.UnmarshalJSON([]byte(body), &ubds) + require.Nil(t, err) + + return ubds +} + +// GET /stake/delegators/{delegatorAddr}/redelegations Get all redelegations from a delegator +func getDelegatorRedelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Redelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/redelegations", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var reds []stake.Redelegation + + err := cdc.UnmarshalJSON([]byte(body), &reds) + require.Nil(t, err) + + return reds +} + +// GET /stake/delegators/{delegatorAddr}/validators Query all validators that a delegator is bonded to +func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Validator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var bondedValidators []stake.Validator + + err := cdc.UnmarshalJSON([]byte(body), &bondedValidators) + require.Nil(t, err) + + return bondedValidators +} + +// GET /stake/delegators/{delegatorAddr}/validators/{validatorAddr} Query a validator that a delegator is bonded to +func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Validator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var bondedValidator stake.Validator + err := cdc.UnmarshalJSON([]byte(body), &bondedValidator) + require.Nil(t, err) + + return bondedValidator +} + +// GET /stake/delegators/{delegatorAddr}/txs Get all staking txs (i.e msgs) from a delegator +func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { + var res *http.Response + var body string + + if len(query) > 0 { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil) + } else { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil) + } + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var txs []tx.Info + + err := cdc.UnmarshalJSON([]byte(body), &txs) + require.Nil(t, err) + + return txs +} + +// GET /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} Query the current delegation between a delegator and a validator +func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var bond stake.Delegation + err := cdc.UnmarshalJSON([]byte(body), &bond) + require.Nil(t, err) + + return bond +} + +// GET /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} Query all unbonding delegations between a delegator and a validator +func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.UnbondingDelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var unbond stake.UnbondingDelegation + err := cdc.UnmarshalJSON([]byte(body), &unbond) + require.Nil(t, err) + + return unbond +} + +// GET /stake/validators Get all validator candidates +func getValidators(t *testing.T, port string) []stake.Validator { + res, body := Request(t, port, "GET", "/stake/validators", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var validators []stake.Validator + err := cdc.UnmarshalJSON([]byte(body), &validators) + require.Nil(t, err) + + return validators +} + +// GET /stake/validators/{validatorAddr} Query the information from a single validator +func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake.Validator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var validator stake.Validator + err := cdc.UnmarshalJSON([]byte(body), &validator) + require.Nil(t, err) + + return validator +} + +// GET /stake/validators/{validatorAddr}/delegations Get all delegations from a validator +func getValidatorDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Delegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/delegations", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var delegations []stake.Delegation + err := cdc.UnmarshalJSON([]byte(body), &delegations) + require.Nil(t, err) + + return delegations +} + +// GET /stake/validators/{validatorAddr}/unbonding_delegations Get all unbonding delegations from a validator +func getValidatorUnbondingDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.UnbondingDelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/unbonding_delegations", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var ubds []stake.UnbondingDelegation + err := cdc.UnmarshalJSON([]byte(body), &ubds) + require.Nil(t, err) + + return ubds +} + +// GET /stake/validators/{validatorAddr}/redelegations Get all outgoing redelegations from a validator +func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Redelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/redelegations", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var reds []stake.Redelegation + err := cdc.UnmarshalJSON([]byte(body), &reds) + require.Nil(t, err) + + return reds +} + +// GET /stake/pool Get the current state of the staking pool +// SEE TestPoolParamsQuery + +// GET /stake/parameters Get the current staking parameter values +// SEE TestPoolParamsQuery + +// ---------------------------------------------------------------------- +// ICS 22 - Gov +// ---------------------------------------------------------------------- +// POST /gov/proposals Submit a proposal +func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { + + acc := getAccount(t, port, proposerAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + pr := postProposalReq{ + Title: "Test", + Description: "test", + ProposalType: "Text", + Proposer: proposerAddr, + InitialDeposit: sdk.Coins{sdk.NewCoin(stakeTypes.DefaultBondDenom, sdk.NewInt(amount))}, + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + } + + req, err := cdc.MarshalJSON(pr) + require.NoError(t, err) + + // submitproposal + res, body := Request(t, port, "POST", "/gov/proposals", req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results +} + +type postProposalReq struct { + BaseReq utils.BaseReq `json:"base_req"` + Title string `json:"title"` // Title of the proposal + Description string `json:"description"` // Description of the proposal + ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} + Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer + InitialDeposit sdk.Coins `json:"initial_deposit"` // Coins to add to the proposal's deposit +} + +// GET /gov/proposals Query proposals +func getProposalsAll(t *testing.T, port string) []gov.Proposal { + res, body := Request(t, port, "GET", "/gov/proposals", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +// GET /gov/proposals?depositor=%s Query proposals +func getProposalsFilterDepositor(t *testing.T, port string, depositorAddr sdk.AccAddress) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s", depositorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +// GET /gov/proposals?voter=%s Query proposals +func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?voter=%s", voterAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +// GET /gov/proposals?depositor=%s&voter=%s Query proposals +func getProposalsFilterVoterDepositor(t *testing.T, port string, voterAddr, depositorAddr sdk.AccAddress) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s&voter=%s", depositorAddr, voterAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +// GET /gov/proposals?status=%s Query proposals +func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStatus) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?status=%s", status), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +// POST /gov/proposals/{proposalId}/deposits Deposit tokens to a proposal +func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { + + acc := getAccount(t, port, proposerAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + dr := depositReq{ + Depositor: proposerAddr, + Amount: sdk.Coins{sdk.NewCoin(stakeTypes.DefaultBondDenom, sdk.NewInt(amount))}, + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + } + + req, err := cdc.MarshalJSON(dr) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results +} + +type depositReq struct { + BaseReq utils.BaseReq `json:"base_req"` + Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor + Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit +} + +// GET /gov/proposals/{proposalId}/deposits Query deposits +func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var deposits []gov.Deposit + err := cdc.UnmarshalJSON([]byte(body), &deposits) + require.Nil(t, err) + return deposits +} + +// GET /gov/proposals/{proposalId}/tally Get a proposal's tally result at the current time +func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/tally", proposalID), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var tally gov.TallyResult + err := cdc.UnmarshalJSON([]byte(body), &tally) + require.Nil(t, err) + return tally +} + +// POST /gov/proposals/{proposalId}/votes Vote a proposal +func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64) (resultTx ctypes.ResultBroadcastTxCommit) { + // get the account to get the sequence + acc := getAccount(t, port, proposerAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + vr := voteReq{ + Voter: proposerAddr, + Option: "Yes", + BaseReq: utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + }, + } + + req, err := cdc.MarshalJSON(vr) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results +} + +type voteReq struct { + BaseReq utils.BaseReq `json:"base_req"` + Voter sdk.AccAddress `json:"voter"` // address of the voter + Option string `json:"option"` // option from OptionSet chosen by the voter +} + +// GET /gov/proposals/{proposalId}/votes Query voters +func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var votes []gov.Vote + err := cdc.UnmarshalJSON([]byte(body), &votes) + require.Nil(t, err) + return votes +} + +// GET /gov/proposals/{proposalId} Query a proposal +func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var proposal gov.Proposal + err := cdc.UnmarshalJSON([]byte(body), &proposal) + require.Nil(t, err) + return proposal +} + +// GET /gov/proposals/{proposalId}/deposits/{depositor} Query deposit +func getDeposit(t *testing.T, port string, proposalID uint64, depositorAddr sdk.AccAddress) gov.Deposit { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var deposit gov.Deposit + err := cdc.UnmarshalJSON([]byte(body), &deposit) + require.Nil(t, err) + return deposit +} + +// GET /gov/proposals/{proposalId}/votes/{voter} Query vote +func getVote(t *testing.T, port string, proposalID uint64, voterAddr sdk.AccAddress) gov.Vote { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var vote gov.Vote + err := cdc.UnmarshalJSON([]byte(body), &vote) + require.Nil(t, err) + return vote +} + +// GET /gov/parameters/deposit Query governance deposit parameters +func getDepositParam(t *testing.T, port string) gov.DepositParams { + res, body := Request(t, port, "GET", "/gov/parameters/deposit", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var depositParams gov.DepositParams + err := cdc.UnmarshalJSON([]byte(body), &depositParams) + require.Nil(t, err) + return depositParams +} + +// GET /gov/parameters/tallying Query governance tally parameters +func getTallyingParam(t *testing.T, port string) gov.TallyParams { + res, body := Request(t, port, "GET", "/gov/parameters/tallying", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var tallyParams gov.TallyParams + err := cdc.UnmarshalJSON([]byte(body), &tallyParams) + require.Nil(t, err) + return tallyParams +} + +// GET /gov/parameters/voting Query governance voting parameters +func getVotingParam(t *testing.T, port string) gov.VotingParams { + res, body := Request(t, port, "GET", "/gov/parameters/voting", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var votingParams gov.VotingParams + err := cdc.UnmarshalJSON([]byte(body), &votingParams) + require.Nil(t, err) + return votingParams +} + +// ---------------------------------------------------------------------- +// ICS 23 - Slashing +// ---------------------------------------------------------------------- +// GET /slashing/validators/{validatorPubKey}/signing_info Get sign info of given validator +func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { + res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/validators/%s/signing_info", validatorPubKey), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var signingInfo slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(body), &signingInfo) + require.Nil(t, err) + + return signingInfo +} + +// POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator +func doUnjail(t *testing.T, port, seed, name, password string, + valAddr sdk.ValAddress) (resultTx ctypes.ResultBroadcastTxCommit) { + chainID := viper.GetString(client.FlagChainID) + + ur := unjailReq{utils.BaseReq{ + Name: name, + Password: password, + ChainID: chainID, + AccountNumber: 1, + Sequence: 1, + }} + req, err := cdc.MarshalJSON(ur) + require.NoError(t, err) + res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err = cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +type unjailReq struct { + BaseReq utils.BaseReq `json:"base_req"` +} From 96f8e340c13dcd147cbb96d8ab6a72532883efad Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 10 Dec 2018 18:32:13 -0800 Subject: [PATCH 14/27] Sacrifice to the linter gods --- client/lcd/test_helpers.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index d716232c6..2b2c543c6 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -663,8 +663,8 @@ func doDelegate(t *testing.T, port, seed, name, password string, Sequence: sequence, }, Delegations: []msgDelegationsInput{msgDelegationsInput{ - DelegatorAddr: sdk.AccAddress(delAddr).String(), - ValidatorAddr: sdk.ValAddress(valAddr).String(), + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), Delegation: sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, amount), }}, } @@ -704,8 +704,8 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, Sequence: sequence, }, BeginUnbondings: []msgBeginUnbondingInput{msgBeginUnbondingInput{ - DelegatorAddr: sdk.AccAddress(delAddr).String(), - ValidatorAddr: sdk.ValAddress(valAddr).String(), + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), SharesAmount: fmt.Sprintf("%d", amount), }}, } @@ -746,9 +746,9 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, Sequence: sequence, }, BeginRedelegates: []msgBeginRedelegateInput{msgBeginRedelegateInput{ - DelegatorAddr: sdk.AccAddress(delAddr).String(), - ValidatorSrcAddr: sdk.ValAddress(valSrcAddr).String(), - ValidatorDstAddr: sdk.ValAddress(valDstAddr).String(), + DelegatorAddr: delAddr.String(), + ValidatorSrcAddr: valSrcAddr.String(), + ValidatorDstAddr: valDstAddr.String(), SharesAmount: fmt.Sprintf("%d", amount), }}, } From affa1fb3f310f58e1d429f0555a0b0b5d085d9e8 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 6 Dec 2018 11:03:58 +0000 Subject: [PATCH 15/27] Reintroduce collect-gentxs's --gentx-dir flag It went lost in last genesis workflow refactoring. Also address structs initializations as per #2835. --- client/flags.go | 1 + cmd/gaia/cli_test/cli_test.go | 31 ++++++++++++++++++++++ cmd/gaia/init/collect.go | 48 ++++++++++++++++++++++++++--------- cmd/gaia/init/gentx.go | 11 +++++--- cmd/gaia/init/init.go | 8 ++---- cmd/gaia/init/testnet.go | 8 +----- x/stake/client/cli/flags.go | 2 -- 7 files changed, 79 insertions(+), 30 deletions(-) diff --git a/client/flags.go b/client/flags.go index 6a3ccff06..cf7be359e 100644 --- a/client/flags.go +++ b/client/flags.go @@ -43,6 +43,7 @@ const ( FlagSSLHosts = "ssl-hosts" FlagSSLCertFile = "ssl-certfile" FlagSSLKeyFile = "ssl-keyfile" + FlagOutputDocument = "output-document" // inspired by wget -O ) // LineBreak can be included in a command list to provide a blank line diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index f7d3b2d83..845edc757 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -645,6 +645,37 @@ trust_node = true cleanupDirs(gaiadHome, gaiacliHome) } +func TestGaiadCollectGentxs(t *testing.T) { + t.Parallel() + // Initialise temporary directories + gaiadHome, gaiacliHome := getTestingHomeDirs(t.Name()) + gentxDir, err := ioutil.TempDir("", "") + gentxDoc := filepath.Join(gentxDir, "gentx.json") + require.NoError(t, err) + + tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") + os.RemoveAll(filepath.Join(gaiadHome, "config", "gentx")) + executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiacli config --home=%s output json", gaiacliHome)) + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --home=%s", gaiacliHome)) + + // Run init + _ = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) + // Add account to genesis.json + executeWriteCheckErr(t, fmt.Sprintf( + "gaiad add-genesis-account %s 150%s,1000fooToken --home=%s", fooAddr, stakeTypes.DefaultBondDenom, gaiadHome)) + executeWrite(t, fmt.Sprintf("cat %s%sconfig%sgenesis.json", gaiadHome, string(os.PathSeparator), string(os.PathSeparator))) + // Write gentx file + executeWriteCheckErr(t, fmt.Sprintf( + "gaiad gentx --name=foo --home=%s --home-client=%s --output-document=%s", gaiadHome, gaiacliHome, gentxDoc), app.DefaultKeyPass) + // Collect gentxs from a custom directory + executeWriteCheckErr(t, fmt.Sprintf("gaiad collect-gentxs --home=%s --gentx-dir=%s", gaiadHome, gentxDir), app.DefaultKeyPass) + cleanupDirs(gaiadHome, gaiacliHome, gentxDir) +} + //___________________________________________________________________________________ // helper methods diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index da97e4626..e630d494c 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -18,6 +18,10 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" ) +const ( + flagGenTxDir = "gentx-dir" +) + type initConfig struct { ChainID string GenTxsDir string @@ -35,7 +39,6 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) name := viper.GetString(client.FlagName) - nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) if err != nil { return err @@ -46,19 +49,13 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return err } - toPrint := printInfo{ - Moniker: config.Moniker, - ChainID: genDoc.ChainID, - NodeID: nodeID, + genTxsDir := viper.GetString(flagGenTxDir) + if genTxsDir == "" { + genTxsDir = filepath.Join(config.RootDir, "config", "gentx") } - initCfg := initConfig{ - ChainID: genDoc.ChainID, - GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), - Name: name, - NodeID: nodeID, - ValPubKey: valPubKey, - } + toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) + initCfg := newInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey) appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) if err != nil { @@ -73,6 +70,9 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { } cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + cmd.Flags().String(flagGenTxDir, "", + "override default \"gentx\" directory from which collect and execute "+ + "genesis transactions; default [--home]/config/gentx/") return cmd } @@ -117,3 +117,27 @@ func genAppStateFromConfig( err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) return } + +func newInitConfig(chainID, genTxsDir, name, nodeID string, + valPubKey crypto.PubKey) initConfig { + + return initConfig{ + ChainID: chainID, + GenTxsDir: genTxsDir, + Name: name, + NodeID: nodeID, + ValPubKey: valPubKey, + } +} + +func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, + appMessage json.RawMessage) printInfo { + + return printInfo{ + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + GenTxsDir: genTxsDir, + AppMessage: appMessage, + } +} diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index c8a7c05d2..9f985762a 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -137,9 +137,12 @@ following delegation and commission default parameters: } // Fetch output file name - outputDocument, err := makeOutputFilepath(config.RootDir, nodeID) - if err != nil { - return err + outputDocument := viper.GetString(client.FlagOutputDocument) + if outputDocument == "" { + outputDocument, err = makeOutputFilepath(config.RootDir, nodeID) + if err != nil { + return err + } } if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { @@ -154,6 +157,8 @@ following delegation and commission default parameters: cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") + cmd.Flags().String(client.FlagOutputDocument, "", + "write the genesis transaction JSON document to the given file instead of the default location") cmd.Flags().AddFlagSet(cli.FsCommissionCreate) cmd.Flags().AddFlagSet(cli.FsAmount) cmd.Flags().AddFlagSet(cli.FsPk) diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 7690d5336..922cfbafa 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -28,6 +28,7 @@ type printInfo struct { Moniker string `json:"moniker"` ChainID string `json:"chain_id"` NodeID string `json:"node_id"` + GenTxsDir string `json:"gentxs_dir"` AppMessage json.RawMessage `json:"app_message"` } @@ -77,12 +78,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return err } - toPrint := printInfo{ - ChainID: chainID, - Moniker: config.Moniker, - NodeID: nodeID, - AppMessage: appState, - } + toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index cfa3e6396..10b0b8265 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -279,13 +279,7 @@ func collectGenFiles( config.SetRoot(nodeDir) nodeID, valPubKey := nodeIDs[i], valPubKeys[i] - initCfg := initConfig{ - ChainID: chainID, - GenTxsDir: gentxsDir, - Name: moniker, - NodeID: nodeID, - ValPubKey: valPubKey, - } + initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey) genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) if err != nil { diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index d571bef9e..97a62ebd9 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -29,8 +29,6 @@ const ( FlagGenesisFormat = "genesis-format" FlagNodeID = "node-id" FlagIP = "ip" - - FlagOutputDocument = "output-document" // inspired by wget -O ) // common flagsets to add to various functions From 46bb2d0deaf007b5d6b2cf2d069fbd7bc930cf31 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 7 Dec 2018 13:45:40 +0000 Subject: [PATCH 16/27] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 3c56e9234..1fd33035b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -34,6 +34,7 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia + * [\#3021](https://github.com/cosmos/cosmos-sdk/pull/3021) Add `--gentx-dir` to `gaiad collect-gentxs` to specify a directory from which collect and load gentxs. * SDK From a051491b564ed21ace7c7c80de3511d42d758a43 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 11 Dec 2018 12:54:45 +0000 Subject: [PATCH 17/27] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 1fd33035b..1acf348e2 100644 --- a/PENDING.md +++ b/PENDING.md @@ -35,6 +35,7 @@ IMPROVEMENTS * Gaia * [\#3021](https://github.com/cosmos/cosmos-sdk/pull/3021) Add `--gentx-dir` to `gaiad collect-gentxs` to specify a directory from which collect and load gentxs. + Add `--output-document` to `gaiad init` to allow one to redirect output to file. * SDK From 4ecbf0dd5f3dd5e1b73bbe84f9463bd34a1540ba Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 11 Dec 2018 15:02:26 +0100 Subject: [PATCH 18/27] Merge PR #2997: Split POST delegations endpoint --- PENDING.md | 3 +- client/context/context.go | 16 +- client/lcd/lcd_test.go | 59 +-- client/lcd/swagger-ui/swagger.yaml | 605 ++++++++++++++++------------- client/utils/rest.go | 24 +- client/utils/utils.go | 4 +- x/bank/client/rest/sendtx.go | 5 +- x/gov/client/rest/rest.go | 15 +- x/ibc/client/rest/transfer.go | 5 +- x/slashing/client/rest/tx.go | 5 +- x/stake/client/rest/tx.go | 303 +++++---------- 11 files changed, 509 insertions(+), 535 deletions(-) diff --git a/PENDING.md b/PENDING.md index 3c56e9234..cc72812a0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3,7 +3,8 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) - * [\#3056](https://github.com/cosmos/cosmos-sdk/pull/3056) `generate_only` and `simulate` have moved from query arguments to POST requests body. + * [gaia-lite] [\#2191](https://github.com/cosmos/cosmos-sdk/issues/2191) Split `POST /stake/delegators/{delegatorAddr}/delegations` into `POST /stake/delegators/{delegatorAddr}/delegations`, `POST /stake/delegators/{delegatorAddr}/unbonding_delegations` and `POST /stake/delegators/{delegatorAddr}/redelegations` + * [gaia-lite] [\#3056](https://github.com/cosmos/cosmos-sdk/pull/3056) `generate_only` and `simulate` have moved from query arguments to POST requests body. * Gaia CLI (`gaiacli`) diff --git a/client/context/context.go b/client/context/context.go index df30374de..9fac7c27a 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -47,7 +47,7 @@ type CLIContext struct { JSON bool PrintResponse bool Verifier tmlite.Verifier - DryRun bool + Simulate bool GenerateOnly bool fromAddress types.AccAddress fromName string @@ -85,7 +85,7 @@ func NewCLIContext() CLIContext { JSON: viper.GetBool(client.FlagJson), PrintResponse: viper.GetBool(client.FlagPrintResponse), Verifier: verifier, - DryRun: viper.GetBool(client.FlagDryRun), + Simulate: viper.GetBool(client.FlagDryRun), GenerateOnly: viper.GetBool(client.FlagGenerateOnly), fromAddress: fromAddress, fromName: fromName, @@ -244,3 +244,15 @@ func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext { ctx.Verifier = verifier return ctx } + +// WithGenerateOnly returns a copy of the context with updated GenerateOnly value +func (ctx CLIContext) WithGenerateOnly(generateOnly bool) CLIContext { + ctx.GenerateOnly = generateOnly + return ctx +} + +// WithSimulation returns a copy of the context with updated Simulate value +func (ctx CLIContext) WithSimulation(simulate bool) CLIContext { + ctx.Simulate = simulate + return ctx +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 34f8edbc9..bc64c2321 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -342,7 +342,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, false, true) + res, body, _ := doSendWithGas(t, port, seed, name, "", addr, "simulate", 0, false, true) require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -1176,15 +1176,9 @@ func doDelegate(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "delegation": { "denom": "%s", "amount": "%d" } - } - ], - "begin_unbondings": [], - "begin_redelegates": [], + "delegator_addr": "%s", + "validator_addr": "%s", + "delegation": { "denom": "%s", "amount": "%d" }, "base_req": { "name": "%s", "password": "%s", @@ -1197,11 +1191,10 @@ func doDelegate(t *testing.T, port, seed, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func doBeginUnbonding(t *testing.T, port, seed, name, password string, @@ -1213,15 +1206,9 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "shares": "%d" - } - ], - "begin_redelegates": [], + "delegator_addr": "%s", + "validator_addr": "%s", + "shares": "%d", "base_req": { "name": "%s", "password": "%s", @@ -1231,14 +1218,13 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, } }`, delAddr, valAddr, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func doBeginRedelegation(t *testing.T, port, seed, name, password string, @@ -1251,16 +1237,10 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [], - "begin_redelegates": [ - { - "delegator_addr": "%s", - "validator_src_addr": "%s", - "validator_dst_addr": "%s", - "shares": "%d" - } - ], + "delegator_addr": "%s", + "validator_src_addr": "%s", + "validator_dst_addr": "%s", + "shares": "%d", "base_req": { "name": "%s", "password": "%s", @@ -1270,14 +1250,13 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, } }`, delAddr, valSrcAddr, valDstAddr, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/redelegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func getValidators(t *testing.T, port string) []stake.Validator { diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 0ecbe505f..a76bdddd6 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -625,71 +625,6 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - post: - summary: Submit delegation - parameters: - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - "$ref": "#/definitions/BaseReq" - delegations: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - delegation: - $ref: "#/definitions/Coin" - begin_unbondings: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" - begin_redelegates: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_src_addr: - $ref: "#/definitions/ValidatorAddress" - validator_dst_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" - tags: - - ICS21 - consumes: - - application/json - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator address or delegation body - 401: - description: Key password is wrong - 500: - description: Internal Server Error get: summary: Get all delegations from a delegator tags: @@ -702,12 +637,72 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/Delegation" + $ref: "#/definitions/Delegation" 400: description: Invalid delegator address 500: description: Internal Server Error + post: + summary: Submit delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + delegation: + $ref: "#/definitions/Coin" + tags: + - ICS21 + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 400: + description: Invalid delegator address or delegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + get: + summary: Query the current delegation between a delegator and a validator + tags: + - ICS21 + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Delegation" + 400: + description: Invalid delegator address or validator address + 500: + description: Internal Server Error /stake/delegators/{delegatorAddr}/unbonding_delegations: parameters: - in: path @@ -727,12 +722,85 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/UnbondingDelegation" + $ref: "#/definitions/UnbondingDelegation" 400: description: Invalid delegator address 500: description: Internal Server Error + post: + summary: Submit an unbonding delegation + parameters: + - in: query + name: simulate + description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it + required: false + type: boolean + - in: query + name: generate_only + description: if true, build an unsigned transaction and write it back + required: false + type: boolean + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" + tags: + - ICS21 + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 400: + description: Invalid delegator address or unbonding delegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + get: + summary: Query all unbonding delegations between a delegator and a validator + tags: + - ICS21 + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/UnbondingDelegation" + 400: + description: Invalid delegator address or validator address + 500: + description: Internal Server Error /stake/delegators/{delegatorAddr}/redelegations: parameters: - in: path @@ -752,12 +820,58 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/Redelegation" + $ref: "#/definitions/Redelegation" 400: description: Invalid delegator address 500: description: Internal Server Error + post: + summary: Submit a redelegation + parameters: + - in: query + name: simulate + description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it + required: false + type: boolean + - in: query + name: generate_only + description: if true, build an unsigned transaction and write it back + required: false + type: boolean + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_src_addr: + $ref: "#/definitions/ValidatorAddress" + validator_dst_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" + tags: + - ICS21 + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 400: + description: Invalid delegator address or redelegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error /stake/delegators/{delegatorAddr}/validators: parameters: - in: path @@ -835,63 +949,6 @@ paths: description: Invalid delegator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - get: - summary: Query the current delegation between a delegator and a validator - tags: - - ICS21 - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Delegation" - 400: - description: Invalid delegator address or validator address - 500: - description: Internal Server Error - /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - get: - summary: Query all unbonding delegations between a delegator and a validator - tags: - - ICS21 - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - type: object - "$ref": "#/definitions/UnbondingDelegation" - 400: - description: Invalid delegator address or validator address - 500: - description: Internal Server Error /stake/validators: get: summary: Get all validator candidates @@ -1116,7 +1173,7 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1147,7 +1204,7 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" + $ref: "#/definitions/BaseReq" title: type: string description: @@ -1156,7 +1213,7 @@ paths: type: string example: "text" proposer: - "$ref": "#/definitions/Address" + $ref: "#/definitions/Address" initial_deposit: type: array items: @@ -1165,7 +1222,7 @@ paths: 200: description: OK schema: - "$ref": "#/definitions/BroadcastTxCommitResult" + $ref: "#/definitions/BroadcastTxCommitResult" 400: description: Invalid proposal body 401: @@ -1201,12 +1258,57 @@ paths: schema: type: array items: - "$ref": "#/definitions/TextProposal" + $ref: "#/definitions/TextProposal" 400: description: Invalid query parameters 500: description: Internal Server Error + /gov/proposals/{proposalId}: + get: + summary: Query a proposal + description: Query a proposal by id + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + name: proposalId + required: true + in: path + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TextProposal" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error /gov/proposals/{proposalId}/deposits: + get: + summary: Query deposits + description: Query deposits by proposalId + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + name: proposalId + required: true + in: path + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Deposit" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error post: summary: Deposit tokens to a proposal description: Send transaction to deposit tokens to a proposal @@ -1230,9 +1332,9 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" + $ref: "#/definitions/BaseReq" depositor: - "$ref": "#/definitions/Address" + $ref: "#/definitions/Address" amount: type: array items: @@ -1241,146 +1343,13 @@ paths: 200: description: OK schema: - "$ref": "#/definitions/BroadcastTxCommitResult" + $ref: "#/definitions/BroadcastTxCommitResult" 400: description: Invalid proposal id or deposit body 401: description: Key password is wrong 500: description: Internal Server Error - get: - summary: Query deposits - description: Query deposits by proposalId - produces: - - application/json - tags: - - ICS22 - parameters: - - type: string - name: proposalId - required: true - in: path - responses: - 200: - description: OK - schema: - type: array - items: - "$ref": "#/definitions/Deposit" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/tally: - get: - summary: Get a proposal's tally result at the current time - description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. - produces: - - application/json - tags: - - ICS22 - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - responses: - 200: - description: OK - schema: - $ref: "#/definitions/TallyResult" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/votes: - post: - summary: Vote a proposal - description: Send transaction to vote a proposal - consumes: - - application/json - produces: - - application/json - tags: - - ICS22 - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` - name: post_vote_body - in: body - required: true - schema: - type: object - properties: - base_req: - "$ref": "#/definitions/BaseReq" - voter: - "$ref": "#/definitions/Address" - option: - type: string - example: "yes" - responses: - 200: - description: OK - schema: - "$ref": "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid proposal id or vote body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - get: - summary: Query voters - description: Query voters information by proposalId - produces: - - application/json - tags: - - ICS22 - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - responses: - 200: - description: OK - schema: - type: array - items: - "$ref": "#/definitions/Vote" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - /gov/proposals/{proposalId}: - get: - summary: Query a proposal - description: Query a proposal by id - produces: - - application/json - tags: - - ICS22 - parameters: - - type: string - name: proposalId - required: true - in: path - responses: - 200: - description: OK - schema: - "$ref": "#/definitions/TextProposal" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error /gov/proposals/{proposalId}/deposits/{depositor}: get: summary: Query deposit @@ -1411,6 +1380,71 @@ paths: description: Found no deposit 500: description: Internal Server Error + /gov/proposals/{proposalId}/votes: + get: + summary: Query voters + description: Query voters information by proposalId + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Vote" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error + post: + summary: Vote a proposal + description: Send transaction to vote a proposal + consumes: + - application/json + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` + name: post_vote_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + voter: + $ref: "#/definitions/Address" + option: + type: string + example: "yes" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 400: + description: Invalid proposal id or vote body + 401: + description: Key password is wrong + 500: + description: Internal Server Error /gov/proposals/{proposalId}/votes/{voter}: get: summary: Query vote @@ -1441,6 +1475,29 @@ paths: description: Found no vote 500: description: Internal Server Error + /gov/proposals/{proposalId}/tally: + get: + summary: Get a proposal's tally result at the current time + description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TallyResult" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error /gov/parameters/deposit: get: summary: Query governance deposit parameters @@ -1538,7 +1595,7 @@ definitions: tags: type: array items: - "$ref": "#/definitions/KVPair" + $ref: "#/definitions/KVPair" example: code: 0 data: data @@ -1567,7 +1624,7 @@ definitions: tags: type: array items: - "$ref": "#/definitions/KVPair" + $ref: "#/definitions/KVPair" example: code: 5 data: data @@ -1868,7 +1925,7 @@ definitions: total_deposit: type: array items: - "$ref": "#/definitions/Coin" + $ref: "#/definitions/Coin" voting_start_time: type: string Deposit: @@ -1877,11 +1934,11 @@ definitions: amount: type: array items: - "$ref": "#/definitions/Coin" + $ref: "#/definitions/Coin" proposal_id: type: integer depositor: - "$ref": "#/definitions/Address" + $ref: "#/definitions/Address" TallyResult: type: object properties: diff --git a/client/utils/rest.go b/client/utils/rest.go index 36ff05af8..5917455ee 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -159,21 +159,21 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i // ValidateBasic performs basic validation of a BaseReq. If custom validation // logic is needed, the implementing request handler should perform those // checks manually. -func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { - switch { - case len(br.Name) == 0: +func (br BaseReq) ValidateBasic(w http.ResponseWriter, cliCtx context.CLIContext) bool { + if !cliCtx.GenerateOnly && !cliCtx.Simulate { + switch { + case len(br.Password) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") + return false + case len(br.ChainID) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") + return false + } + } + if len(br.Name) == 0 { WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified") return false - - case len(br.Password) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") - return false - - case len(br.ChainID) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "chainID required but not specified") - return false } - return true } diff --git a/client/utils/utils.go b/client/utils/utils.go index 08a538eec..6fde3ce6b 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -34,14 +34,14 @@ func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return err } - if txBldr.SimulateGas || cliCtx.DryRun { + if txBldr.SimulateGas || cliCtx.Simulate { txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return err } fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas) } - if cliCtx.DryRun { + if cliCtx.Simulate { return nil } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 7bb2640fd..27cbc043c 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -49,8 +49,11 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 53deffbbe..821da0cc7 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -78,8 +78,11 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -123,8 +126,11 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -162,8 +168,11 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 19c921971..704b663e6 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -43,8 +43,11 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 5f33a1210..ec28f48ed 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -38,8 +38,11 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 5deb5b53c..bb2bc0e7f 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -2,81 +2,72 @@ package rest import ( "bytes" - "io/ioutil" "net/http" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/gorilla/mux" - - ctypes "github.com/tendermint/tendermint/rpc/core/types" ) func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { r.HandleFunc( "/stake/delegators/{delegatorAddr}/delegations", - delegationsRequestHandlerFn(cdc, kb, cliCtx), + postDelegationsHandlerFn(cdc, kb, cliCtx), + ).Methods("POST") + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/unbonding_delegations", + postUnbondingDelegationsHandlerFn(cdc, kb, cliCtx), + ).Methods("POST") + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/redelegations", + postRedelegationsHandlerFn(cdc, kb, cliCtx), ).Methods("POST") } type ( msgDelegationsInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - Delegation sdk.Coin `json:"delegation"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + Delegation sdk.Coin `json:"delegation"` } msgBeginRedelegateInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32 - ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32 - SharesAmount string `json:"shares"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 + ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } msgBeginUnbondingInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - SharesAmount string `json:"shares"` - } - - // the request body for edit delegations - EditDelegationsReq struct { - BaseReq utils.BaseReq `json:"base_req"` - Delegations []msgDelegationsInput `json:"delegations"` - BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` - BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } ) -// TODO: Split this up into several smaller functions, and remove the above nolint -// TODO: use sdk.ValAddress instead of sdk.AccAddress for validators in messages -// TODO: Seriously consider how to refactor...do we need to make it multiple txs? -// If not, we can just use CompleteAndBroadcastTxREST. -func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req EditDelegationsReq + var req msgDelegationsInput - body, err := ioutil.ReadAll(r.Body) + err := utils.ReadRESTReq(w, r, cdc, &req) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - err = cdc.UnmarshalJSON(body, &req) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -86,182 +77,98 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte return } - // build messages - messages := make([]sdk.Msg, len(req.Delegations)+ - len(req.BeginRedelegates)+ - len(req.BeginUnbondings)) - - i := 0 - for _, msg := range req.Delegations { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - - messages[i] = stake.MsgDelegate{ - DelegatorAddr: delAddr, - ValidatorAddr: valAddr, - Delegation: msg.Delegation, - } - - i++ + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") + return } - for _, msg := range req.BeginRedelegates { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - - valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - shares, err := sdk.NewDecFromStr(msg.SharesAmount) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - messages[i] = stake.MsgBeginRedelegate{ - DelegatorAddr: delAddr, - ValidatorSrcAddr: valSrcAddr, - ValidatorDstAddr: valDstAddr, - SharesAmount: shares, - } - - i++ - } - - for _, msg := range req.BeginUnbondings { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - - valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - shares, err := sdk.NewDecFromStr(msg.SharesAmount) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - messages[i] = stake.MsgBeginUnbonding{ - DelegatorAddr: delAddr, - ValidatorAddr: valAddr, - SharesAmount: shares, - } - - i++ - } - - simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas) + msg := stake.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) + err = msg.ValidateBasic() if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) - if !ok { + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) + } +} + +func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req msgBeginRedelegateInput + + err := utils.ReadRESTReq(w, r, cdc, &req) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - txBldr := authtxb.TxBuilder{ - Codec: cdc, - Gas: gas, - GasAdjustment: adjustment, - SimulateGas: simulateGas, - ChainID: baseReq.ChainID, + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w, cliCtx) { + return } - // sign messages - signedTxs := make([][]byte, len(messages[:])) - for i, msg := range messages { - // increment sequence for each message - txBldr = txBldr.WithAccountNumber(baseReq.AccountNumber) - txBldr = txBldr.WithSequence(baseReq.Sequence) - - baseReq.Sequence++ - - if baseReq.Simulate || txBldr.SimulateGas { - newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg}) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if baseReq.Simulate { - utils.WriteSimulationResponse(w, newBldr.Gas) - return - } - - txBldr = newBldr - } - - if baseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, txBldr, []sdk.Msg{msg}) - return - } - - txBytes, err := txBldr.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg}) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } - - signedTxs[i] = txBytes + info, err := kb.Get(baseReq.Name) + if err != nil { + utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) + return } - // send - // XXX the operation might not be atomic if a tx fails - // should we have a sdk.MultiMsg type to make sending atomic? - results := make([]*ctypes.ResultBroadcastTxCommit, len(signedTxs[:])) - for i, txBytes := range signedTxs { - res, err := cliCtx.BroadcastTx(txBytes) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - results[i] = res + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") + return } - utils.PostProcessResponse(w, cdc, results, cliCtx.Indent) + msg := stake.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) + } +} + +func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req msgBeginUnbondingInput + + err := utils.ReadRESTReq(w, r, cdc, &req) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w, cliCtx) { + return + } + + info, err := kb.Get(baseReq.Name) + if err != nil { + utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) + return + } + + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") + return + } + + msg := stake.NewMsgBeginUnbonding(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) } } From e54e0465e294a0e729edbc0529af05d8f836358f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 11 Dec 2018 15:49:19 +0100 Subject: [PATCH 19/27] Merge PR #3080: Run fewer blocks on CI multi-seed simulation --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c194aa84f..d866bd637 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -178,7 +178,7 @@ jobs: name: Test multi-seed Gaia simulation command: | export PATH="$GOBIN:$PATH" - make test_sim_gaia_multi_seed + scripts/multisim.sh 25 TestFullGaiaSimulation test_cover: <<: *defaults From e46afe00d6979c7a035e2361e5f685463903b59b Mon Sep 17 00:00:00 2001 From: zramsay Date: Tue, 11 Dec 2018 12:12:29 -0500 Subject: [PATCH 20/27] make sidebar titles nicer --- docs/gaia/join-testnet.md | 2 +- docs/gaia/ledger.md | 2 +- docs/gaia/validators/security.md | 4 ++-- docs/gaia/validators/validator-setup.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/gaia/join-testnet.md b/docs/gaia/join-testnet.md index e5cfcf4d2..b09b8fc1c 100644 --- a/docs/gaia/join-testnet.md +++ b/docs/gaia/join-testnet.md @@ -1,4 +1,4 @@ -# Join the latest public testnet +# Join Public Testnet ::: tip Current Testnet See the [testnet repo](https://github.com/cosmos/testnets) for diff --git a/docs/gaia/ledger.md b/docs/gaia/ledger.md index e4bf9ac15..ce03c88b2 100644 --- a/docs/gaia/ledger.md +++ b/docs/gaia/ledger.md @@ -1,4 +1,4 @@ -# Ledger // Cosmos +# Ledger Nano Support ### Ledger Support for account keys diff --git a/docs/gaia/validators/security.md b/docs/gaia/validators/security.md index 5ccf98814..a5072d719 100644 --- a/docs/gaia/validators/security.md +++ b/docs/gaia/validators/security.md @@ -1,4 +1,4 @@ -## Overview +## Validator Security Each validator candidate is encouraged to run its operations independently, as diverse setups increase the resilience of the network. Validator candidates should commence their setup phase now in order to be on time for launch. @@ -49,4 +49,4 @@ By default, uppercase environment variables with the following prefixes will rep - `BC` (for democli or basecli flags) For example, the environment variable `GA_CHAIN_ID` will map to the command line flag `--chain-id`. Note that while explicit command-line flags will take precedence over environment variables, environment variables will take precedence over any of your configuration files. For this reason, it's imperative that you lock down your environment such that any critical parameters are defined as flags on the CLI or prevent modification of any environment variables. - \ No newline at end of file + diff --git a/docs/gaia/validators/validator-setup.md b/docs/gaia/validators/validator-setup.md index 12a63dd84..99858f429 100644 --- a/docs/gaia/validators/validator-setup.md +++ b/docs/gaia/validators/validator-setup.md @@ -1,4 +1,4 @@ -# Run a validator on the gaia public testnet +# Run a Validator on Public Testnet ::: tip Information on how to join the current testnet (`genesis.json` file and seeds) is held [in our `testnet` repo](https://github.com/cosmos/testnets/tree/master/latest). Please check there if you are looking to join our latest testnet. From 2f9b062cf3d101b5775fe2b2a3cc708612feeb09 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Tue, 11 Dec 2018 11:05:49 -0800 Subject: [PATCH 21/27] Merge PR #3015: Add Governance genesis checks * missing genesis verification in gaia, also add distribution verification * PENDING.md * Fix error message --- cmd/gaia/app/sim_test.go | 5 +++-- x/gov/genesis.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index bf03f9a10..b4504bdf8 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -76,14 +76,15 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { } // Random genesis states + vp := time.Duration(r.Intn(2*172800)) * time.Second govGenesis := gov.GenesisState{ StartingProposalID: uint64(r.Intn(100)), DepositParams: gov.DepositParams{ MinDeposit: sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, int64(r.Intn(1e3)))}, - MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + MaxDepositPeriod: vp, }, VotingParams: gov.VotingParams{ - VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + VotingPeriod: vp, }, TallyParams: gov.TallyParams{ Threshold: sdk.NewDecWithPrec(5, 1), diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 8498e3b5a..7a8fab0b6 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -1,6 +1,7 @@ package gov import ( + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -61,6 +62,34 @@ func DefaultGenesisState() GenesisState { // ValidateGenesis TODO https://github.com/cosmos/cosmos-sdk/issues/3007 func ValidateGenesis(data GenesisState) error { + threshold := data.TallyParams.Threshold + if threshold.IsNegative() || threshold.GT(sdk.OneDec()) { + return fmt.Errorf("Governance vote threshold should be positive and less or equal to one, is %s", + threshold.String()) + } + + veto := data.TallyParams.Veto + if veto.IsNegative() || veto.GT(sdk.OneDec()) { + return fmt.Errorf("Governance vote veto threshold should be positive and less or equal to one, is %s", + veto.String()) + } + + govPenalty := data.TallyParams.GovernancePenalty + if govPenalty.IsNegative() || govPenalty.GT(sdk.OneDec()) { + return fmt.Errorf("Governance vote veto threshold should be positive and less or equal to one, is %s", + govPenalty.String()) + } + + if data.DepositParams.MaxDepositPeriod > data.VotingParams.VotingPeriod { + return fmt.Errorf("Governance deposit period should be less than or equal to the voting period (%ds), is %ds", + data.VotingParams.VotingPeriod, data.DepositParams.MaxDepositPeriod) + } + + if !data.DepositParams.MinDeposit.IsValid() { + return fmt.Errorf("Governance deposit amount must be a valid sdk.Coins amount, is %s", + data.DepositParams.MinDeposit.String()) + } + return nil } From 206566f0433f247a1dd4848d62b1ae379890f56c Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Tue, 11 Dec 2018 11:34:02 -0800 Subject: [PATCH 22/27] Adjust for redelegation and unbonding changes --- client/lcd/lcd_test.go | 12 +++--- client/lcd/test_helpers.go | 82 ++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 8d7c2c93b..e72f6836c 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -433,7 +433,7 @@ func TestValidatorQuery(t *testing.T) { } func TestBonding(t *testing.T) { - addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) + addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr}) defer cleanup() @@ -445,7 +445,7 @@ func TestBonding(t *testing.T) { validator := getValidator(t, port, operAddrs[0]) // create bond TX - resultTx := doDelegate(t, port, seed, name1, pw, addr, operAddrs[0], 60) + resultTx := doDelegate(t, port, name1, pw, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -485,7 +485,7 @@ func TestBonding(t *testing.T) { require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr) // testing unbonding - resultTx = doBeginUnbonding(t, port, seed, name1, pw, addr, operAddrs[0], 30) + resultTx = doBeginUnbonding(t, port, name1, pw, addr, operAddrs[0], 30) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -508,7 +508,7 @@ func TestBonding(t *testing.T) { require.Equal(t, "30", unbonding.Balance.Amount.String()) // test redelegation - resultTx = doBeginRedelegation(t, port, seed, name1, pw, addr, operAddrs[0], operAddrs[1], 30) + resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], 30) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -670,7 +670,7 @@ func TestVote(t *testing.T) { require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 as the address is not bonded") // create bond TX - resultTx = doDelegate(t, port, seed, name1, pw, addr, operAddrs[0], 60) + resultTx = doDelegate(t, port, name1, pw, addr, operAddrs[0], 60) tests.WaitForHeight(resultTx.Height+1, port) // vote @@ -814,4 +814,4 @@ func TestProposalsQuery(t *testing.T) { require.Len(t, votes, 2) require.True(t, addrs[0].String() == votes[0].Voter.String() || addrs[0].String() == votes[1].Voter.String()) require.True(t, addrs[1].String() == votes[0].Voter.String() || addrs[1].String() == votes[1].Voter.String()) -} \ No newline at end of file +} diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 2b2c543c6..b678d2789 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -647,14 +647,14 @@ type editDelegationsReq struct { } // POST /stake/delegators/{delegatorAddr}/delegations Submit delegation -func doDelegate(t *testing.T, port, seed, name, password string, +func doDelegate(t *testing.T, port, name, password string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - ed := editDelegationsReq{ + ed := msgDelegationsInput{ BaseReq: utils.BaseReq{ Name: name, Password: password, @@ -662,40 +662,36 @@ func doDelegate(t *testing.T, port, seed, name, password string, AccountNumber: accnum, Sequence: sequence, }, - Delegations: []msgDelegationsInput{msgDelegationsInput{ - DelegatorAddr: delAddr.String(), - ValidatorAddr: valAddr.String(), - Delegation: sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, amount), - }}, + DelegatorAddr: delAddr, + ValidatorAddr: valAddr, + Delegation: sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, amount), } req, err := cdc.MarshalJSON(ed) require.NoError(t, err) - - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr.String()), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - - var results []ctypes.ResultBroadcastTxCommit + var results ctypes.ResultBroadcastTxCommit err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) - - return results[0] + return results } type msgDelegationsInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - Delegation sdk.Coin `json:"delegation"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + Delegation sdk.Coin `json:"delegation"` } // POST /stake/delegators/{delegatorAddr}/delegations Submit delegation -func doBeginUnbonding(t *testing.T, port, seed, name, password string, +func doBeginUnbonding(t *testing.T, port, name, password string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - ed := editDelegationsReq{ + ed := msgBeginUnbondingInput{ BaseReq: utils.BaseReq{ Name: name, Password: password, @@ -703,33 +699,32 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, AccountNumber: accnum, Sequence: sequence, }, - BeginUnbondings: []msgBeginUnbondingInput{msgBeginUnbondingInput{ - DelegatorAddr: delAddr.String(), - ValidatorAddr: valAddr.String(), - SharesAmount: fmt.Sprintf("%d", amount), - }}, + DelegatorAddr: delAddr, + ValidatorAddr: valAddr, + SharesAmount: sdk.NewDec(amount), } req, err := cdc.MarshalJSON(ed) require.NoError(t, err) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations", delAddr), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit + var results ctypes.ResultBroadcastTxCommit err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) - return results[0] + return results } type msgBeginUnbondingInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - SharesAmount string `json:"shares"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } // POST /stake/delegators/{delegatorAddr}/delegations Submit delegation -func doBeginRedelegation(t *testing.T, port, seed, name, password string, +func doBeginRedelegation(t *testing.T, port, name, password string, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { acc := getAccount(t, port, delAddr) @@ -737,7 +732,7 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - ed := editDelegationsReq{ + ed := msgBeginRedelegateInput{ BaseReq: utils.BaseReq{ Name: name, Password: password, @@ -745,31 +740,30 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, AccountNumber: accnum, Sequence: sequence, }, - BeginRedelegates: []msgBeginRedelegateInput{msgBeginRedelegateInput{ - DelegatorAddr: delAddr.String(), - ValidatorSrcAddr: valSrcAddr.String(), - ValidatorDstAddr: valDstAddr.String(), - SharesAmount: fmt.Sprintf("%d", amount), - }}, + DelegatorAddr: delAddr, + ValidatorSrcAddr: valSrcAddr, + ValidatorDstAddr: valDstAddr, + SharesAmount: sdk.NewDec(amount), } req, err := cdc.MarshalJSON(ed) require.NoError(t, err) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), req) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/redelegations", delAddr), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit + var results ctypes.ResultBroadcastTxCommit err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) - return results[0] + return results } type msgBeginRedelegateInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32 - ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32 - SharesAmount string `json:"shares"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 + ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } // GET /stake/delegators/{delegatorAddr}/delegations Get all delegations from a delegator From 490a8d61c0047fdf18e50c820105a611389462b5 Mon Sep 17 00:00:00 2001 From: zramsay Date: Fri, 7 Dec 2018 10:21:24 -0500 Subject: [PATCH 23/27] update release test script links, closes #2606 --- docs/RELEASE_TEST_SCRIPT.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/RELEASE_TEST_SCRIPT.md b/docs/RELEASE_TEST_SCRIPT.md index 725507958..c1eb093d8 100644 --- a/docs/RELEASE_TEST_SCRIPT.md +++ b/docs/RELEASE_TEST_SCRIPT.md @@ -1,17 +1,17 @@ This document should contain plain english instructions for testing functionality on `gaiad`. This “Script” is supposed to be run by 2 people who will each spin up a `gaiad` node and run the series of prompts below. -- [Create a network of 2 nodes](getting-started/create-testnet.md) -- [Generate an account](sdk/clients.md) -- [Send funds from one account to the other](sdk/clients.md) -- [Create a validator](validators/validator-setup.md) -- [Edit a validator](validators/validator-setup.md) -- [Delegate to validator](sdk/clients.md) -- [Unbond from a validator](sdk/clients.md) -- [View validators and verify output](validators/validator-setup.md) -- [Query network status](getting-started/full-node.md) -- [Create a proposal](validators/validator-setup.md) -- [Query a proposal](validators/validator-setup.md) -- [Vote on a proposal](validators/validator-setup.md) -- [Query status of a proposal](validators/validator-setup.md) -- [Query the votes on a proposal](validators/validator-setup.md) -- [Export state and reload](getting-started/create-testnet.md) +- [Create a network of 2 nodes](./gaia/deploy-testnet.md) +- [Generate an account](./gaia/gaiacli.md) +- [Send funds from one account to the other](./gaia/gaiacli.md) +- [Create a validator](./gaia/validators/validator-setup.md) +- [Edit a validator](./gaia/alidators/validator-setup.md) +- [Delegate to validator](./gaia/gaiacli.md) +- [Unbond from a validator](./gaia/gaiacli.md) +- [View validators and verify output](./gaia/validators/validator-setup.md) +- [Query network status](./gaia/join-testnet.md#run-a-full-node) +- [Create a proposal](./gaia/validators/validator-setup.md) +- [Query a proposal](./gaia/validators/validator-setup.md) +- [Vote on a proposal](./gaia/validators/validator-setup.md) +- [Query status of a proposal](./gaia/validators/validator-setup.md) +- [Query the votes on a proposal](./gaia/validators/validator-setup.md) +- [Export state and reload](./gaia/join-testnet.md) From 6f08a0e76a1488225ed060508ae3518ec30f7466 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Tue, 11 Dec 2018 13:04:59 -0800 Subject: [PATCH 24/27] Address PR comments --- client/lcd/lcd_test.go | 120 ++++++----------------------- client/lcd/test_helpers.go | 150 +++++++++++++++++++++++++++++++++---- 2 files changed, 157 insertions(+), 113 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index e72f6836c..164edb0f3 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -13,14 +13,11 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - p2p "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" client "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" tests "github.com/cosmos/cosmos-sdk/tests" @@ -123,86 +120,26 @@ func TestVersion(t *testing.T) { func TestNodeStatus(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() - - // node info - res, body := Request(t, port, "GET", "/node_info", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var nodeInfo p2p.DefaultNodeInfo - err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) - require.Nil(t, err, "Couldn't parse node info") - - require.NotEqual(t, p2p.DefaultNodeInfo{}, nodeInfo, "res: %v", res) - - // syncing - 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" - require.Equal(t, "false", body) + getNodeInfo(t, port) + getSyncStatus(t, port, false) } func TestBlock(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() - - var resultBlock ctypes.ResultBlock - - res, body := Request(t, port, "GET", "/blocks/latest", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err := cdc.UnmarshalJSON([]byte(body), &resultBlock) - require.Nil(t, err, "Couldn't parse block") - - require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) - - // -- - - res, body = Request(t, port, "GET", "/blocks/2", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err = codec.Cdc.UnmarshalJSON([]byte(body), &resultBlock) - require.Nil(t, err, "Couldn't parse block") - - require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) - - // -- - - res, body = Request(t, port, "GET", "/blocks/1000000000", nil) - require.Equal(t, http.StatusNotFound, res.StatusCode, body) + getBlock(t, port, -1, false) + getBlock(t, port, 2, false) + getBlock(t, port, 100000000, true) } func TestValidators(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() - - var resultVals rpc.ResultValidatorsOutput - - res, body := Request(t, port, "GET", "/validatorsets/latest", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err := cdc.UnmarshalJSON([]byte(body), &resultVals) - require.Nil(t, err, "Couldn't parse validatorset") - - require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) - + resultVals := getValidatorSets(t, port, -1, false) require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosvaloper") require.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalconspub") - - // -- - - res, body = Request(t, port, "GET", "/validatorsets/2", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - err = cdc.UnmarshalJSON([]byte(body), &resultVals) - require.Nil(t, err, "Couldn't parse validatorset") - - require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) - - // -- - - res, body = Request(t, port, "GET", "/validatorsets/1000000000", nil) - require.Equal(t, http.StatusNotFound, res.StatusCode, body) + getValidatorSets(t, port, 2, false) + getValidatorSets(t, port, 10000000, true) } func TestCoinSend(t *testing.T) { @@ -222,7 +159,7 @@ func TestCoinSend(t *testing.T) { initialBalance := acc.GetCoins() // create TX - receiveAddr, resultTx := doSend(t, port, seed, name1, pw, addr) + receiveAddr, resultTx := doTransfer(t, port, seed, name1, pw, addr) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -246,29 +183,29 @@ func TestCoinSend(t *testing.T) { require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "100", 0, false, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, "100", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with negative gas - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "-200", 0, false, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, "-200", 0, false, false) require.Equal(t, http.StatusBadRequest, res.StatusCode, body) // test failure with 0 gas - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "0", 0, false, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, "0", 0, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // test failure with wrong adjustment - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "simulate", 0.1, false, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, "simulate", 0.1, false, false) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, "", 0, true, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, "", 0, true, false) require.Equal(t, http.StatusOK, res.StatusCode, body) var responseBody struct { GasEstimate int64 `json:"gas_estimate"` } require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) - res, body, _ = doSendWithGas(t, port, seed, name1, pw, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, false, false) + res, body, _ = doTransferWithGas(t, port, seed, name1, pw, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, false, false) require.Equal(t, http.StatusOK, res.StatusCode, body) } @@ -279,7 +216,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name1, pw, addr, "simulate", 0, false, true) + res, body, _ := doTransferWithGas(t, port, seed, name1, pw, addr, "simulate", 0, false, true) require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -288,11 +225,11 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr}) require.Equal(t, 0, len(msg.Signatures)) gasEstimate := msg.Fee.Gas + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() // sign tx var signedMsg auth.StdTx - accnum := acc.GetAccountNumber() - sequence := acc.GetSequence() payload := authrest.SignBody{ Tx: msg, @@ -352,13 +289,12 @@ func TestTxs(t *testing.T) { require.Equal(t, emptyTxs, txs) // create tx - receiveAddr, resultTx := doSend(t, port, seed, name1, pw, addr) + receiveAddr, resultTx := doTransfer(t, port, seed, name1, pw, addr) tests.WaitForHeight(resultTx.Height+1, port) // check if tx is queryable - txs = getTransactions(t, port, fmt.Sprintf("tx.hash=%s", resultTx.Hash)) - require.Len(t, txs, 1) - require.Equal(t, resultTx.Hash, txs[0].Hash) + tx := getTransaction(t, port, resultTx.Hash.String()) + require.Equal(t, resultTx.Hash, tx.Hash) // query sender txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String())) @@ -378,26 +314,16 @@ func TestPoolParamsQuery(t *testing.T) { defaultParams := stake.DefaultParams() - res, body := Request(t, port, "GET", "/stake/parameters", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - - var params stake.Params - err := cdc.UnmarshalJSON([]byte(body), ¶ms) - require.Nil(t, err) + params := getStakeParams(t, port) require.True(t, defaultParams.Equal(params)) - res, body = Request(t, port, "GET", "/stake/pool", nil) - require.Equal(t, http.StatusOK, res.StatusCode, body) - require.NotNil(t, body) + pool := getStakePool(t, port) initialPool := stake.InitialPool() initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewDec(100)) initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState initialPool.LooseTokens = initialPool.LooseTokens.Add(sdk.NewDec(int64(50))) // freeFermionsAcc = 50 on GaiaAppGenState - var pool stake.Pool - err = cdc.UnmarshalJSON([]byte(body), &pool) - require.Nil(t, err) require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) require.Equal(t, initialPool.LooseTokens, pool.LooseTokens) } diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index b678d2789..91364132b 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -15,12 +15,12 @@ import ( "strings" "testing" - "github.com/tendermint/tendermint/crypto/secp256k1" - cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/tendermint/tendermint/crypto/secp256k1" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/cosmos/cosmos-sdk/client" @@ -420,24 +420,92 @@ func Request(t *testing.T, port, method, path string, payload []byte) (*http.Res // ICS 0 - Tendermint // ---------------------------------------------------------------------- // GET /node_info The properties of the connected node -// SEE TestNodeStatus +func getNodeInfo(t *testing.T, port string) p2p.DefaultNodeInfo { + res, body := Request(t, port, "GET", "/node_info", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var nodeInfo p2p.DefaultNodeInfo + err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) + require.Nil(t, err, "Couldn't parse node info") + + require.NotEqual(t, p2p.DefaultNodeInfo{}, nodeInfo, "res: %v", res) + return nodeInfo +} // GET /syncing Syncing state of node -// SEE TestNodeStatus +func getSyncStatus(t *testing.T, port string, syncing bool) { + res, body := Request(t, port, "GET", "/syncing", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + if syncing { + require.Equal(t, "true", body) + return + } + require.Equal(t, "false", body) +} // GET /blocks/latest Get the latest block -// SEE TestBlock - // GET /blocks/{height} Get a block at a certain height -// SEE TestBlock +func getBlock(t *testing.T, port string, height int, expectFail bool) ctypes.ResultBlock { + var url string + if height > 0 { + url = fmt.Sprintf("/blocks/%d", height) + } else { + url = "/blocks/latest" + } + var resultBlock ctypes.ResultBlock -// GET /validatorsets/latest Get the latest validator set -// SEE TestValidators + res, body := Request(t, port, "GET", url, nil) + if expectFail { + require.Equal(t, http.StatusNotFound, res.StatusCode, body) + return resultBlock + } + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &resultBlock) + require.Nil(t, err, "Couldn't parse block") + + require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) + return resultBlock +} // GET /validatorsets/{height} Get a validator set a certain height -// SEE TestValidators +// GET /validatorsets/latest Get the latest validator set +func getValidatorSets(t *testing.T, port string, height int, expectFail bool) rpc.ResultValidatorsOutput { + var url string + if height > 0 { + url = fmt.Sprintf("/validatorsets/%d", height) + } else { + url = "/validatorsets/latest" + } + var resultVals rpc.ResultValidatorsOutput + + res, body := Request(t, port, "GET", url, nil) + + if expectFail { + require.Equal(t, http.StatusNotFound, res.StatusCode, body) + return resultVals + } + + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &resultVals) + require.Nil(t, err, "Couldn't parse validatorset") + + require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) + return resultVals +} // GET /txs/{hash} get tx by hash +func getTransaction(t *testing.T, port string, hash string) tx.Info { + var tx tx.Info + res, body := Request(t, port, "GET", fmt.Sprintf("/txs/%s", hash), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &tx) + require.NoError(t, err) + return tx +} + // POST /txs broadcast txs // GET /txs search transactions @@ -571,14 +639,47 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { // ICS 20 - Tokens // ---------------------------------------------------------------------- -// TODO: TEST THE FOLLOWING ROUTES // POST /tx/sign Sign a Tx +func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence uint64, msg auth.StdTx) auth.StdTx { + var signedMsg auth.StdTx + payload := authrest.SignBody{ + Tx: msg, + LocalAccountName: name, + Password: password, + ChainID: chainID, + AccountNumber: accnum, + Sequence: sequence, + } + json, err := cdc.MarshalJSON(payload) + require.Nil(t, err) + res, body := Request(t, port, "POST", "/tx/sign", json) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg)) + return signedMsg +} + // POST /tx/broadcast Send a signed Tx +func doBroadcast(t *testing.T, port string, msg auth.StdTx) ctypes.ResultBroadcastTxCommit { + tx := broadcastReq{Tx: msg, Return: "block"} + req, err := cdc.MarshalJSON(tx) + require.Nil(t, err) + res, body := Request(t, port, "POST", "/tx/broadcast", req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var resultTx ctypes.ResultBroadcastTxCommit + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx)) + return resultTx +} + +type broadcastReq struct { + Tx auth.StdTx `json:"tx"` + Return string `json:"return"` +} + // GET /bank/balances/{address} Get the account balances // POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send) -func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { - res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, false, false) +func doTransfer(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { + res, body, receiveAddr := doTransferWithGas(t, port, seed, name, password, addr, "", 0, false, false) require.Equal(t, http.StatusOK, res.StatusCode, body) err := cdc.UnmarshalJSON([]byte(body), &resultTx) @@ -587,7 +688,7 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress return receiveAddr, resultTx } -func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, +func doTransferWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, gasAdjustment float64, simulate, generateOnly bool) ( res *http.Response, body string, receiveAddr sdk.AccAddress) { @@ -935,10 +1036,26 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA } // GET /stake/pool Get the current state of the staking pool -// SEE TestPoolParamsQuery +func getStakePool(t *testing.T, port string) stake.Pool { + res, body := Request(t, port, "GET", "/stake/pool", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NotNil(t, body) + var pool stake.Pool + err := cdc.UnmarshalJSON([]byte(body), &pool) + require.Nil(t, err) + return pool +} // GET /stake/parameters Get the current staking parameter values -// SEE TestPoolParamsQuery +func getStakeParams(t *testing.T, port string) stake.Params { + res, body := Request(t, port, "GET", "/stake/parameters", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var params stake.Params + err := cdc.UnmarshalJSON([]byte(body), ¶ms) + require.Nil(t, err) + return params +} // ---------------------------------------------------------------------- // ICS 22 - Gov @@ -1230,6 +1347,7 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. return signingInfo } +// TODO: Test this functionality, it is not currently in any of the tests // POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator func doUnjail(t *testing.T, port, seed, name, password string, valAddr sdk.ValAddress) (resultTx ctypes.ResultBroadcastTxCommit) { From d8528ee4e8f500336bb2c65dff26bbd30e70d531 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Tue, 11 Dec 2018 13:30:03 -0800 Subject: [PATCH 25/27] Remove unused code --- client/lcd/test_helpers.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 91364132b..58b8426e5 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -740,13 +740,6 @@ type sendReq struct { // ICS 21 - Stake // ---------------------------------------------------------------------- -type editDelegationsReq struct { - BaseReq utils.BaseReq `json:"base_req"` - Delegations []msgDelegationsInput `json:"delegations"` - BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` - BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"` -} - // POST /stake/delegators/{delegatorAddr}/delegations Submit delegation func doDelegate(t *testing.T, port, name, password string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { From a31dc20e6a9fd63564252b9f6d45dbd133f667c7 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 12 Dec 2018 04:13:53 -0800 Subject: [PATCH 26/27] Merge PR #2990: Update f1 spec * Update f1 spec: * Improves exposition of the base scheme * Fix inflation section / add sketch of how to do it * Add future work section I've also realized that using the ideas of the inflation section, we could even remove the iteration over slashes when withdrawing! (Note, inflation section equations / proof of correctness has not been thoroughly transcribed from my head onto paper, so its not guaranteed to be correct at the moment. However the inflation section of this describes something separate from inflation in the hub. It has inflation happen by having each staked token produce more staked tokens, instead of having it come through block rewards. This inflation can also happen per validator, so as to enable non-signing validators to not get inflation) * Fix grammar Co-Authored-By: ValarDragon * Add proof of correctness to the inflation section * Re-update PDF after merging chris' fixes * address more of @cwgoes' suggestions --- .../f1-fee-distribution/f1_fee_distr.pdf | Bin 160589 -> 183348 bytes .../f1-fee-distribution/f1_fee_distr.tex | 194 ++++++++++++++---- 2 files changed, 154 insertions(+), 40 deletions(-) diff --git a/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.pdf b/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.pdf index 12e7d5906e58b2efa6f94679c342d341854d2f6f..0f7443104dec707e04b18c3180a1a65616b46db5 100644 GIT binary patch delta 128197 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 J6IYZ#{6A9Cq16BY delta 105473 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 J(Xh*!@r z7X>308sWeaQm9k>k3YSZmMDVE_WH^E>0niCjigVB-D^4K> z#gl>sp5|Vc?T5IbhP$jSbHV`O9Ij7Gx`jRID%=Xuv}itN0^ovhCVI}E{~ib`B1xMbRg03wBx zcu0M<^lYF5W4sbu6p}|qrI$;crW|V(VZ@q=pIpUIrx70*PV8>g8jLHCZxa(3rhj~z z;IH+OypX+-UU5N<1$9_N8QbA-DjnkWsvZA)^V98pPna)%_1TfTDM18=^XIu&h9nqB z4edDG1p#K9hwLKGYsbLDD%%H3C#~_p9~m@@#_L^(3Wh_!YQG!q2Z;@K(NN0pfwx|p z@Mkp&rM!g>^PEBeG3GIs*f(`ZuW$q<>zX z0J++Q{^DYPgU6~84MD%EGFw=1`dENDm84`c2a3*k6M?K6I=KX_8MNMmQP@IIzm#`1 zw7PhbD)ML?fdxwUWO=rvc*SGcp#extPHErW=Z_j6{d&X?d5veOD+ms5Ddcu>c zR2$xC=N^`tNzp^5v5kY|KyCW+w%!A^1Z5RjdZOFcyCS`~sX)J5FX5c@x}1mPQ>8jw zicZW^l8tulkBWUU^xtGxhIzV@#?;o2Y41UQpCHMDDyMNP!<+&T!5I3(w)MGJmgZLi zkVvVYXfRvI)7W!Y}fsprRwKp2h>i)MP`hi$#&9Yk$OALj~~Q#Z3B;39iM%6;pl z$qA^j0b@!MdWB_|pYP^}9(9%kVkL1to{PfaEVE1o(v^M&$o0Y%J+R9d=sFr6v+L=9 zxZ^NL_Wjy{lT^BB3^52_ZYu0j#@d#|ZVdz~wW2zYZ(BJPXu(8WQ9j#Y0Rzx0%S+Dk= z`-xB@GrC}>g3Lu6IS{4g=$C^}LmDi9L#JhUR0o2% zMrv|!>$U0nZS$w;8S*Keaa+4d_^Z7-m|Mg{RviZHpD@Kvu@>!}_nH4-yYAAU9uGdVF5j8Z^*^%tT$R)SRrwaJvq-6G*dP z9Dw@@%x*u*r2`g_P(#Ox>%7o^zA1@<+9%&{$t|1d8O*yZPU7niS|yAhd#q>&7_F|sD-GqEA-fXaziOMqQZkyM>zT*5?_jAVTOhksHB~xos>jSc}w4< zLQp-)q1cax*V+o+Nv32R{rRg7)7GPm{iL^KYoNzA6bZN;mRUppa>qDTIkAD6rw z3Me(NqBCdK52FsX4OX3=sZ^o&k&hu#y;B6X2Tza!#}ZD@g)(E(5?S#kyj^;&r=I1S z`;(DQSps}5PE1ID)0x?z-k^wgFAi5+k}m4pLuw}yEIxStIF{8d5)I&!z78#myF>B5 zh;dbHxkb2B%zWkMYv8#?-%=*}hIXc@nZiP?Y`AUNGK6`P|3h{Zr0`N^H4-E?5@V9@ zEE>By;$Zs&?ga4FH3enFKcsAkkG{13_EcN<&=uC!-I$wy4==S4{b?U@*g90c;wPUt z^LsBvme8dNfVF9C$OC~?l|>-mlhK0xiq}Pb((u7_gDlc_oEXUg3N7rk@-h1#!SI2C z);9;GNuTS3BX8N@!Q+lP6yjo3VqPMW_5v|F*lFAn+%Xcy``Sc5Tkp*p6RGV#)DuS( z;ARu!;+?>MQ#-ia;`eC*b<{M#MScn_%Nth@ZnzbjYnJ=MUtTx!Hd|Wgvt^;0rmN*B z%(cNXoqaD0T!5Z%<{}W+!XUWb!WgK7hgo-CeziF~;!vm6i8n`sEwO6}EAWw;q5#>7P6tR-Oa^@L;M}>qJn3S}Omue?gKZ(S2vR&JV%B%pEH`Rm&!2Gbv$G z=L8P-bNLUx0!Bl8zD0U69r^MI=(vx5`Id3%ZPBQ2Ua##(UTDV9!EG}Yw_Fe825Ht* zI)1W$Q(?|UQF;5k@onhbob1Y=W$bWJy!)ruJyh@&5bj=z4kI~K9BISq)s}dy2WC=| zkl$sz919Iu11+>u_&%OL{QFdTlv}a-VdxyzoU~bE&6vls{#DuSFF!B*Y*sQ)#3aY! zM`GR&p{N0Lz9^ua`M?feWG`-BcaZi@Qm~A z2+wGpG>^`x^IDC%!g)Ay`XfSe&AuT&Sw^l}5j!4H z3-)~IJn^QpZ&%Tmp1brNYU9jhtO>S%rlh!qrs7e6$N;q7q5)nu5YOuByWilQjIuSG z*=h6vl-aVNnN8Nm0=6}6Dw;{By#TvUkvh%7IZh4JJKcK|Y|_<3(v*0iT_$M6HKlUFW(S$uTc zIT@yqEAf{eCzp|n%C<0Jw=k5`b4SJuw5h|}QOVJ>@D34d+GVdJEc^)ayQiUS3M4#n zyOw_9XD%}%bL?6bz-Hc{@b;D!ud_SPvkO*coo{+jw@O&cFIq3#RX!2YC@R}{VVRk) zV;^UI`aU!1JTKTgDEfTuwnXxOGNWXE4!c5_m@cb;tWY_6(z@H~#~kx@;21!=m#db2FdmhCG?VSAALaW0p#9^ zHDVMmvAuCHBAvL%Cx0GI;yMC@M6&o-cIftV>sk2=Q%o??r zCDl>Gd%q%9H-zIs4D3JE##A_Z5UP!Mo%2rob$f3w37T!ukoV88o7nGHv+2yvtXa(5UZ?Cg8Oj+y(!H8 z1x;ggM1n@?I<8be{0r)TneP`JEjv1VTR8eu9pcs6pMkj|6>EcMxcK1~3b%UD>s3Fp z4!z5+fU-96{oEz%qdngm+f!-1R?!kh%8azAU*~)8Z_R=exLlojzu|Ud9NEcSCVgIs zwiCSJz2EY!H@2n-QM@l?=ef1O>xdh6^`&@1l(7($II9=G=J1eztocga%gOY3M~ja2 z^YBQ!Ri^%J56mGWPFRU_Xzt$TZCheZL4@!$VzC2(-gEu>0bR%h$*xK5N5yVg9M}5Q z6-u79Tt~=kY>Iq8B$d2lWHR1&TJFnzz$A=xli~Voh zx`9b?tr3%?GAB`g28?H^%{6|F5i@t3L8paZU=kbTVK=%}rN>$6Sg1_JF0bHh={l6I zBuID!x}st@g1;QU4w{$s5XhtvUuiX1TTDdM+gv~ltE>`eSU1HEsa2j$v2jaPiYxM8 z*q4ZXD_diexAt8f_qbklp}u4))A5>+nJ(Tov&zFs(E5^p57pd5`MBa0d^lSCDlL1p zK;mJa4BU8~-eIa><@`mH-I^?;tulpR5?=4A_Jf(fL+@0059#S@-rlA`i!Hlw@YGxq zrv{bN8**M*D~;5eWfrBz`e)(l(h}sP1Z4cP5IIy^sRFHEy0?VbFpSTH#WIZuq11Ow zarQD_dAf^#RRk@#3Kwxu?7E=0-ZN|JIB8N|@mM@H*np#~3oFjNYU3T>gqMXi8|X5$ z`wv{>N@0G2oV-`Wi!Y(7^*4a^<2=;i!0MCi!g&xzr87I5-|~9<+PK$X=$*`o-#h=%75**@L-5?9C$kdt$``5s`(F zS6Et6yv#Ch{J#_}B!(GL)&bEhm}&eCJc%BLgS%#<^SauDBHxNPw$@4DiWAeY1{%PI zNggF)vM4NkRTDs@`@#ur6a5|$!}JG6@>#WQkhs-bQtbXN3O)qtlK6&*-k`fq*nOfC zHX+@AQ7~^S1I^+OE*)6-wD+GDVZhVtWmltx_U%k)5yr<55rHv_UT2JRKWj1%WKVK{d$+XG$I^MMml&d-fvQi>6xO;xqQPNRz-1t3-7i~WS*=||M zPd4gL1O?l4f!RmwJt&fdr7m9;6Crh;-7|rIW{pOCn^&P&WArhgrE2PJ98JS=;X9U1 z{EIq=wKRgNG*b8aFDh?YZtrlubK`RJPO((Sd1tbz*SyL1ZiT-izog8CsiGe9p!{~u z(Ncew-Wpehdpe(BwJ*k(=oORkKC>f-X%JQl40DQiu%ue1-!~Fm5CYE9j%3eTntSel zTh5tIjPmpyA6F`&bSwG0Dfd*X_1Tf^DlCesB%g6*-hBOq+3cR7g1E45NlccBRIcQEdWk* zHAmz!|kJ<9|F;T@hkQz~tpO|16<(J_f6L+0C=`QdxhEFEY z_DX@y=+R-9a%o9%CFHx40tfVe{?;2^h_cZkMWshQ8Pprpy^Ecd);D~CmMzRM}v!f2fp~SwFiV9h)@aS634^5Z0=O2xKjp>B~7x+gz z=-#djDRV*rSS#qtuF>n^d9W3G;cze}*XIK_dwQ^dl{w=SPQEwgmMCngapQax#W{rS zyM~gOf~--zdpq_T>+r#oV~m&r@P~qexrMoop+KFGZN6BDpRvoZX9lT2f=gQmBmTQL zTJY+T{Kz02?tjKG~IsiJF1$LZ&t%6E#gv*aM30?Kk};NU2!?$H!St= z1z>SqPAf5?S&zDWjgWRRaGe<>-Mh9yqnnw!PgH}XX3wFDZI}~_29u;mvHU($vGA5= z!U$j0ruM7(+~{E`JL&hJ{H1>I%@!3>Y1n9I4=40CNgrWn-uUt&A z64_*~CEqm?yRhf=0K|Ee4tB zpcHHmI4&cU zTC$iftiAiStbR4{4x|{Xdl?sPdiWwPgJJYca|-u^-30G)B0rnE)|Qxwv#G>S=RNN% ziG_Q3W$L_#2F=9F$8725k|_>UBTRhE$(>%3u6}BXmaF&T7Rq!3>A-otPD<$s%OqqVNgZG5-jAIkXY!p*+M& zSd1v7$y{cOtD`VDQM}_>O(ZT{KG(FBO=@>?$h|_9Q71`V0}pJ0&;&}jPc8lKpgJWV z_S;u~>W*=Ep*iWZ@5wx*n7_TJrn%H_&;K_2Jh*4adeNb-CHYO~ULL*u2`==LuWI03 z+UTvr+;zKNSzeWBo~NDgyW6Q+J*Dzn1l-?t?Sl4IBXI@ZPuUOQ2)R+OclA^6 zq^v1I`-NU|1wS#9?q%A1SPMsJ3;gEcJ~nIQ2aQo6$E9@W-QAioSziPlz?xu2^g63V z*<_KF2~CuZvK!ByRP5H*79EKS5e%Q&(TC`7Q$&bntEy9 zz*Y=tsEH1=#ND#+XAogPEt7=KeE$xo24aI-t(B9ETW^>mjCz0|zEBM|ETuL6UREY< zoRnqkP;8b*Z2!xR+LM15VkB-G>9VdD6Up`K-#} zod_99&@(3&a&1w8J%hgY$7sP()D3o;RyNQx&&JsCa4)qN?&OxvoR6v6DkHychY56@ zR^wzsQ$NdXlMHrBzY5~Py#|}8eC%1haor#pQ7@70Rl?A{xCLn+vw#8ClA|dd15eTn z+9CizH8jT)m?a)~cRwzt-47st6Pe-%@IRG=&^-rgv0a=bIQqIGXjFCx^=Em`$CT$= z$v7PT#P#Dg8mHOp&3b>KuH}bgOV0Pe=eVHzYKdwK9;x_kHOFLwR_eIUS47ADMAlD| zT5L`D#b@+MHZe|yn`rAL;j8>mTguwE?iWX_^r&07Kr78H7&;@*-a=Y`XKu$Egl)4G z!59u6uJD?TT$IzD;)m?@^ByZe(+G za%Ev{3T19&Z(?c#3OO>9(fSp$N{J#L0y#93(fSpatX={Q8#FmH3NK7$ZfA68GaxVu zFHB`_XLM*FH#9gmmjR#wDSvbaG@M=6wk`zGOA-vCw;8?nn&`bv7-KNX3}e($qKh8U z61|1!1c}~)AOt~3)F{zQw21aeo;>gSKJWjp^?mDJ>%Q-^uWRqK_u1Dz>@0eQ{EBu+ zTbLRWf#wGZ0HpxR+Nx$CAOHvy761Z;h}qeV;Aj`v-*#el6Bx<^j(!uzkdN-P;LN7Qc|4nw>v=56^4RCAqapr1nmfOz1|TDaRC@2p>P=5_a7m+ zWE|0GHz`3uZ*Ok_h<~ex021XO$IS=uhNB$;1~3m8$_r)(_*F7M2jU9*Qzhuz&kSgt&Mhul*rj5V#A( z_L}fJbqGLBQ6B)guJBKN9#9nA4ecS|0eAUTqu{SF*T<}iuv135y221>58_|>slZV% z==Hh#3jS%XGXm+2!2G?kha>Flf0be9=_Y85fV+FbG*$j^u1&=MwmHDi08yZ%khqu_ z0Ok&W`9K{7e}8RY=0^s(r>kl!;1L6e(pi!Q%0L*^^ z|6CD+Kma>96b-P2IlvLb|BilbhS~qMzV1H??gOv{Uds;z0RDRZ^#{IIm>m+~;`?v< z?-mQ{XgyF@*WvwB^1nbOC8Q4k!!INP;1`k<0|G!IqJJU)ad9C)z&``&LEwK9`44AJ zggp`<`B%Qz2laQ(UVlWu^=Cr30soApgS?h548ZkorCR|-fzaz0=>M$u-!A_@ng5FN zzbgH|Gg9+(arupN{YChHaEL41#rF^6TDqR->oL$qUJnD}zp19Mzh+k(W(W6l{cltg z4Y?i%MSp~Y%fAj1?x6~6!Vwe_}8qtz!5M#qzC-h?E>Hj0fGO8T@M)4 z`T7R&xR&K_2zEU`|J+g)0Y%#V8Z;qMF#rUGg7^|&xA}S{3c!G_C(;h)^IOOOK>-93 zeeD9cE-wIJk3Z!00K+x^_9zeLj?9oba2Y-iO@L!1VALMrr{{cxz00cceU0r|Q zigs`hHy4QSzd_)CfM}Qx`Zw~Aq@kXu>$vFO!n&^L@AdDi0tWMeL5Zg)kx=O{r`oWV zAJvL?y!ls#Wk0g7n5J{{W2RB9ow-LMjK6|darv#Z^ zKGhHJx9UExdP={%kC4_D0hpYRd~Y{dx|VJsGe;!h|1W`E0#ggiPJ?lD->-e9gl z-@S7-?;MpYYuh~_yo(#bVP$TFVe={{b?pbV5U7fZH>n5C=aKM+2wV0Pg+CXM6@Tu@ zqLt$C)X4)YEfd{`QQRyXzVtfl;-dT-g(H3+OOs4ZR}kOXf?FBqg>&#zzp`gM;zeej zUf$2&lExNKNBnT$n2aemKHs6{RjDvlEb3X9AAiNP&A8k*p_k<&UBC`TWl<;;E?^U0 z#ph7usS~DDEb6I-GwY@7%Gv*z# zC}Vn|Yc@kpR6^_a$ox4VjNl8ccV6G8MTMuh(6pDFnlz)^$v@jE^L<|j*MIbI&)Ilt z7Emarj1oE^x7Z1f5UDE(Z0zUs+**27l|r;pZ)}ULx9GuX2ot{XpC?pzLtC9J$7wWb z$6uL*YbQC`N)!$022BN`k1Ui{o^$3?^k5&p8;_g-55Jrq7k-JNLx!5SZ@Aqn9Oa>Ao8JBxCm zesZ^0TW?98=!|B1@qhmARj|N)Ze&!T!o%3CsTGjZ;C{FIYsnW|pHFfp$Bdq20j-IZ z9P3iUo3bw838t-Qqe!XOuj?W=3RR!RRKUL!Z(AlPK1}^^aZDQ3XMdR{cxy$~U`YDi zzQKB&bTS!huNYU)%U2KX$Sbo9$w@mna<@&8s6!fei6jyFFPUdMWI`0^R`(*ZxTu(A zK4VA4W!&-Sjo=QL(CZzHq!!|MS!2hP>h20XpJ!XrBy+57SnFOs8{qB^+GhH`#2o!Yj7$xR!+mb?_Om{p^eKzgNX7whTP6lz$R*I&c+`7W7eE=L>fa zKl+Hf@L`#%CfCE6ByG!E^MJ9|H=9QGC6;ZQ?RN(KX4f&fGk-Tx^UlIH&e27j&mygNO>F9S0*Nhv|p*O<}s!g{I{QZhQXZ>hDieOh+B_mC44d#=ZA^1d!G zPl+!B1hfD%8lA;WC<{@;q-7O8iq9c_R*4b&GvpjiFwO9(QUvGL{|w_yF* z$vc9}vg)uDkIU#nPA|>wyf)lO$`%s)+<$9}l_=y5`xot(U3iK~Vv`X%n4s`?>`f`) zIC_<{S{vzAInB+MSE~iRD-I1TnR&{F@U^M#bfo=Ap95XA3%#clQaIN)mC<-_HviL} z6(k3rwo@8^i}UHiWBW3N#f)cOQ(w!YFD)*roNnqU)q+EdsP_bX-@uPdg}cv)M1L_+ zvG4A&Vo^!oS^s2y4lawg$z_3q_A%$RH5Osjh+F;#Smj|RhJ0RCVlzEo1^OVa_xzXG zsi);e$G&cJ8wDx1e@h*AW-WLk%uKz6tX1GKF#g0BKPeWqEOUoVq)X(6O{|Yf@7`N% zq?GC;`U?|AJ4}pCDl#pi@;TW1+kag4QHHW&X?6;y6F5)y?CgO^6#gOOj;dm}Y+=o? z)S$NRIHY%s*Tk&fa_NOF-z=jmWSXZjnm%brOftR)BUH1I*5nl=XQ zwXe38?>#k)65{S1VPt;HG%2{uzl5vnRU?YqL(!?=8}u?JL=pgQVjH=_{(m(3?(_iL zColqowaY>@Sm4($V}W&qj2A%L_9@vn-QLr0SP0A>=*q8VA1gPA^0y*UfPjw zBifcGQy>HWh4iE9v+P&$8h>#*bgwZBecP+Jj(2i;HPdF;T;O%PYre0Z(oNsEcZH&w zm#ITDWJJTwup!;K+aIV2gmPpqUZp0%`o265c{KDmL325Ll=S7oCz{yU6)*wgXZq?+ zK3uhf6RB#Ays4x~7jsgzR1L$R9UA!EcTa9dcQ8n$s8gFS9bSYreScSfh3&VE^%!Gn zfn^A?Tz=a?WX4)T5oYvxfTsSZlwJFq8Wvcic6P|8?W%z)FD*Qyn=cqBQ`)U4mLDZD z_zGgJ&m3%+xKAQY{Vbqg14RQ^)}9&BHZ{J|sVBOjnp7G;anr=@S>oof>%k%aGST8- zZc1d_on<=J$hz^^3V+GO3ECQc;8HvjWdNUGK5KG~>CA|WZ3{_Z4yC;TgGAPV1cOPe zJdKgIm9(*S=ZaQZP|wy)Copn4+<`IwEs9|0!x&GY|Bv<-C!*&L$t1zjIdEPsU;72B z9Y+flEE23eyWqyuCg2|H_G&YUXF--7-xCkL&n5QU4m%2wA%8gPx)1!0QivZduXZ#N z8RkFSOFkM);$t?Luut4-xK!=3%BZ}`gJV~-dc8DG8

*|pT)H~6?BId#_=%dRcr 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

O^R#%n^YrjcDGREP6c$}`o_`oN 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 TIxW74z~d>$V`Y`sRKWW$tcckX diff --git a/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.tex b/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.tex index 969e77ec9..2a4c2ba39 100644 --- a/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.tex +++ b/docs/spec/spec-proposals/f1-fee-distribution/f1_fee_distr.tex @@ -2,7 +2,7 @@ \usepackage{hyperref} %opening -\title{F1 Fee Distribution Draft-00} +\title{F1 Fee Distribution Draft-01} \author{Dev Ojha} \begin{document} @@ -17,49 +17,71 @@ \section{F1 Fee Distribution} -In a proof of stake model, each validator has an associated stake, with delegators each contributing some amount to a validator's stake. -The validator is rewarded transaction fees every block for the service they are providing the network. -In the F1 distribution, each validator is permitted to take a commission from the fees they receive, and the remaining fees should be evenly distributed across the validator's delegators, such that every delegator the percentage of the validator's stake that came from the delegator is the proportion of that validator's remaining tx fees which they are getting. -Iterating over all delegators for every validator each block is too expensive for a blockchain environment. -Instead there is an explicit withdraw fees action which a delegator can take, which will give the delegator the same total amount of fees as though they were receiving it every block. +\subsection{Context} +In a proof of stake blockchain, each validator has an associated stake. +Transaction fees get rewarded to validators based on the incentive scheme of the underlying proof of stake model. +The fee distribution problem occurs in proof of stake blockchains supporting delegation, as there is a need to distribute a validator's fee rewards to its delegators. +The trivial solution of just giving the rewards to each delegator every block is too expensive to perform on-chain. +So instead fee distribution algorithms have delegators perform an explicit withdraw transaction, which when performed yields the same total amount of fees as if they had received them at every block. -Suppose a delegator delegates $x$ stake to a validator at block $h$. +This details F1, an approximation-free, slash-tolerant fee distribution algorithm which allows validator commission-rates, inflation rates, and fee proportions, which can all efficiently change per validator, every block. +The algorithm requires iterating over the validators every block, and withdraws require iterating over all of the corresponding validator's slashes whilst the delegator was bonded. +The former iteration is cheap, due to staking logic already requiring iteration over all validators, which causes the expensive state-reads to be cached. +The number of slashes is expected to be 0 or 1 for most validators, +so the latter term also meets the blockchain's efficiency needs. + +The key point of how F1 works is that it tracks how much rewards a delegator with 1 stake for a given validator would be entitled to if it had bonded at block 0 until the latest block. +When a delegator bonds at block $b$, the amount of rewards a delegator with 1 stake would have if bonded at block 0 until block $b$ is also persisted to state. +When the delegator withdraws, they receive the difference of these two values. +Since rewards are distributed according to stake-weighting, this amount of rewards can be scaled by the amount of stake a delegator had. +Section 1.2 describes this in more detail, with an argument for it being approximation free. +Section 2 details how to adapt this algorithm to handle commission rates, slashing, and inflation. + +\subsection{Base algorithm} +In this section, we show that the F1 base algorithm gives each delegator rewards identical to that which they'd receive in the naive and correct fee distribution algorithm that iterated over all delegators every block. + +Even distribution of a validators rewards amongst its validators weighted by stake means the following: +Suppose a delegator delegates $x$ stake to a validator $v$ at block $h$. Let the amount of stake the validator has at block $i$ be $s_i$ and the amount of fees they receive at this height be $f_i$. -Then if a delegator contributing $x$ stake decides to withdraw at block $n$, the rewards they receive is +Then if a delegator contributing $x$ stake decides to withdraw at block $n$, the rewards they receive are $$\sum_{i = h}^{n} \frac{x}{s_i}f_i = x \sum_{i = h}^{n} \frac{f_i}{s_i}$$ -However $s_i$ will not change every block. -It only changes if the validator gets slashed, or if someone new has bonded or unbonded. -Handling slashes is relegated to \autoref{ssec:slashing}. -Define a period as the set of blocks between two changes in a given validator's total stake. +Note that $s_i$ does not change every block, +it only changes if the validator gets slashed, +or if any delegator alters the amount they have delegated. +We'll relegate handling of slashes to \autoref{ssec:slashing}, +and only consider the case with no slashing here. +We can change the iteration from being over every block, to instead being over the set of blocks between two changes in validator $v$'s total stake. +Let each of these set of blocks be called a period. A new period begins every time that validator's total stake changes. -The above iteration will be converted to iteration over periods. Let the total amount of stake for the validator in period $p$ be $n_p$. -Let $T_p$ be the total fees this validator accrued within this period. +Let $T_p$ be the total fees that validator $v$ accrued in period $p$. Let $h$ be the start of period $p_{init}$, and height $n$ be the end of $p_{final}$. It follows that $$x \sum_{i = h}^{n} \frac{f_i}{s_i} = x \sum_{p = p_{init}}^{p_{final}} \frac{T_p}{n_p}$$ -Let $p_0$ represent the period from when the validator first bonded until the first change to the validators stake. -The central idea to the F1 model is that at the end of the $k$th period, the following is stored at a state location indexable by $k$: $\sum_{i=0}^{k}\frac{T_i}{n_i}$. -When a delegator wants to delegate or withdraw their reward, they first create a new entry in state to end the current period. Let the index of the current period be $f$. -Then this entry is created using the previous entry as follows: $$\sum_{i=0}^{f}\frac{T_i}{n_i} = \sum_{i=0}^{f-1}\frac{T_i}{n_i} + \frac{T_f}{n_f} = entry_{f-1} + \frac{T_f}{n_f}$$ +Let $p_0$ represent the period which begins when the validator first bonds. +The central idea to the F1 model is that at the end of the $k$th period, +the following is stored at a state location indexable by $k$: $\sum_{i=0}^{k}\frac{T_i}{n_i}$. +Let the index of the current period be $f$. +When a delegator wants to delegate or withdraw their reward, they first create a new entry in state to end the current period. +Then this entry is created using the previous entry as follows: +$$Entry_f = \sum_{i=0}^{f}\frac{T_i}{n_i} = \sum_{i=0}^{f-1}\frac{T_i}{n_i} + \frac{T_f}{n_f} = Entry_{f-1} + \frac{T_f}{n_f}$$ Where $T_f$ is the fees the validator has accrued in period $f$, and $n_f$ is the validators total amount of stake in period $f$. -The withdrawer's delegation object has the index $k$ for the period which they started accruing fees for. -Thus the reward they should receive when withdrawing is: +The withdrawer's delegation object has the index $k$ for the period which they ended by bonding. (They start receiving rewards for period $k + 1$) +The reward they should receive when withdrawing is: -$$x\left(entry_f - entry_k\right) = x\left(\left(\sum_{i=0}^{f}\frac{T_i}{n_i}\right) - \left(\sum_{i=0}^{k}\frac{T_i}{n_i}\right)\right) = x \sum_{i = k}^{f} \frac{T_i}{n_i}$$ +$$x \sum_{i = k + 1}^{f} \frac{T_i}{n_i} = x\left(\left(\sum_{i=0}^{f}\frac{T_i}{n_i}\right) - \left(\sum_{i=0}^{k}\frac{T_i}{n_i}\right)\right) = x\left(Entry_f - Entry_k\right)$$ -The first summation is the state entry for $f$, and the second sum is the state entry at $k$. -It is clear from the equations that this payout mechanism maintains correctness, and required no iterations. +It is clear from the equations that this payout mechanism maintains correctness, and requires no iterations. It just needed the two state reads for these entries. $T_f$ is a separate variable in state for the amount of fees this validator has accrued since the last update to its power. This variable is incremented at every block by however much fees this validator received that block. -On the update to the validators power, this variable is used to create the entry in state at $f$. +On the update to the validators power, this variable is used to create the entry in state at $f$, and is then reset to 0. This fee distribution proposal is agnostic to how all of the blocks fees are divied up between validators. -This creates many nice properties, for example only rewarding validators who signed that block. +This creates many nice properties, for example it is possible to only rewarding validators who signed that block. \section{Additional add-ons} \subsection{Commission Rates} @@ -68,13 +90,14 @@ This can easily be done as follows: In block $h$ a validator receives $f_h$ fees. Instead of incrementing that validators ``total accrued fees this period variable" by $f_h$, it is instead incremented by $(1 - commission\_rate) * f_p$. -Then $commission\_rate * f_p$ is deposited directly to the validator. -This scheme allow for updates to a validator's commission rate every block if desired. +Then $commission\_rate * f_p$ is deposited directly to the validator's account. +This allows for efficient updates to a validator's commission rate every block if desired. +More generally, each validator could have a function which takes their fees as input, and outputs a set of outputs to pay these fees too. (i.e. x\% going to themselves, y\% to delegators, z\% burnt) \subsection{Slashing} \label{ssec:slashing} -Slashing is distinct from withdrawals, since not only does it lower the validators total amount of stake, but it also lowers each of its delegator's stake by a fixed percentage. -Since noone is charged gas for slashes, a slash cannot iterate over all delegators. +Slashing is distinct from withdrawals, since it lowers the stake of all of the delegator's by a fixed percentage. +Since no one is charged gas for slashes, a slash cannot iterate over all delegators. Thus we can no longer just multiply by $x$ over the difference in stake. The solution here is to instead store each period created by a slash in the validators state. Then when withdrawing, you must iterate over all slashes between when you started and ended. @@ -85,24 +108,94 @@ When there are multiple slashes, you just account for the accumulated slash fact In practice this will not really be an efficiency hit, as we can expect most validators to have no slashes. Validators that get slashed a lot will naturally lose their delegators. -A malicious validator that gets itself slashed many times would increase the gas to withdraw linearly, but the economic loss of funds due to the slashes should far out-weigh the extra overhead the withdrawer must pay for due to the gas. +A malicious validator that gets itself slashed many times would increase the gas to withdraw linearly, but the economic loss of funds due to the slashes should far out-weigh the extra overhead the honest withdrawer must pay for due to the gas. \subsection{Inflation} -Inflation is the idea that we want every staked coin to grow in value as time progresses. Each block, every staked token should each be rewarded $x$ staking tokens as inflation, where $x$ is calculated from function which takes state and the block information as input. This also allows for many seemless upgrade's to $x$'s algorithm. This can be added efficiently into the fee distribution model as follows: +Inflation is the idea that we want every staked coin to create more staking tokens as time progresses. +The purpose being to drive down the relative worth of unstaked tokens. +Each block, every staked token should produce $x$ staking tokens as inflation, where $x$ is calculated from a function $inflation$ which takes state and the block information as input. +Let $x_i$ represent the evaluation of $inflation$ in the $i$th block. +The goal of this section is to auto-bond inflation in the fee distribution model without iteration. +This is done by preserving the invariant that every state entry contains the rewards one would have if they had bonded one stake at genesis until that corresponding block. -Make each block have an inflation number, by which every staked token should produce $x$ additional staking tokens. In state there is a variable for the sum of all such inflation numbers. Then each period will store this total inflation sum in addition to $\sum_{i=0}^{end}\frac{T_i}{n_i}$. When withdrawing perform a subtraction on the inflation sums at the end and at the start to see how many new staking tokens to produce per staked token. +In state a variable should be kept for the number of tokens one would have now due to inflation, +given that they bonded one token at genesis. +This is $\prod_{0}^{now} (1 + x_i)$. +Each period now stores this total inflation product along with what it already stores per-period. -This works great in the model where the inflation rate should be dynamic each block, but apply the same to each validator. Inflation creation can trivially be epoched as long as inflation isn't required within the epoch, through changes to the $x$ function. +Let $R_i$ be the fee rewards in block $i$, and $n_i$ be the total amount bonded to that validator in that block. +The correct amount of rewards which 1 token at genesis should have now is: +$$Reward(now) = \sum_{i = 0}^{now}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i}$$ +The term in the sum is the amount of stake one stake becomes due to inflation, multiplied by the amount of fees per stake. -Note that this process is extremely efficient. +Now we cast this into the period frame of view. +Recall that we build the rewards by creating a state entry for the rewards of the previous period, and keeping track of the rewards within this period. +Thus we first define the correct amount of rewards for each successive period, proving correctness of this via induction. +We then show that the state entry that gets efficiently built up block by block is equal to this value for the latest period. -The above can be trivially amended if we want inflation to proceed differently for different validators each block. (e.g. depending on who was offline) It can also be made to be easily adapted in a live chain. It is unclear if either of these two are more desirable settings. +Let $start, end$ denote the start/end of a period. + +Suppose that $\forall f > 0$, $Reward(end(f))$ is correctly constructed as +$$Reward(end(f)) = Reward(end(f-1)) + \sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$ +and that for $f = 0$, $Reward(end(0)) = 0$. +(With period 1 being defined as the period that has the first bond into it) +It must be shown that assuming the supposition $\forall f \leq f_0$, $$Reward(end(f_0 + 1)) = Reward(end(f_0)) + \sum_{i = start(f_0 + 1)}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$ +Using the definition of $Reward$, it follows that: +$$\sum_{i = 0}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i} = \sum_{i = 0}^{end(f_0)}\left(\prod_{j = 0}^{i} 1 + x_j \right) * \frac{R_i}{n_i} + \sum_{i = start(f_0 + 1)}^{end(f_0 + 1)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$ + +Since the first summation on the right hand side is $Reward(end(f_0))$, the supposition is proven true. +Consequently, the reward for just period $f$ adjusted for the amount of inflation 1 token at genesis would produce, is: +$$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$ + +TODO: make this proof + pre-amble less verbose, and just wrap up into a lemma. +Maybe just leave this proof or the last part to the reader, since it easily follows from summation bounds. + +Now note that +$$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i} = \left(\prod_{j = 0}^{end(f - 1)} 1 + x_j \right)\sum_{i = start(f)}^{end(f)}\left(\prod_{j = start(f)}^{i} 1 + x_j \right) \frac{R_i}{n_i}$$ +By definition of period, and inflation being applied every block, \\ +$n_i = n_{start(f)}\left(\prod_{j = start(f)}^{i} 1 + x_j \right)$. This cancels out the product in the summation, therefore +$$\sum_{i = start(f)}^{end(f)}\left(\prod_{j = 0}^{i} 1 + x_j \right) \frac{R_i}{n_i} = \left(\prod_{j = 0}^{end(f - 1)} 1 + x_j \right)\frac{\sum_{i = start(f)}^{end(f)}R_i}{n_{start(f)}}$$ + +Thus every block, each validator just has to add the total amount of fees (The $R_i$ term) that goes to delegates to some per-period term. +When creating a new period, $n_{start(f)}$ can be cached in state, and the product is already stored in the previous periods state entry. +You then get the next period's $n_{start(f)}$ from the current tm-power entry. +This is thus extremely efficient per block. + +When withdrawing, you take the difference as before, +which yields the amount of rewards you would have obtained with $(\prod_0^{begin\ bonding\ period}1 + x)$ stake from the block you began bonding at until now. +$(\prod_0^{begin\ bonding\ period}1 + x)$ is known, since its included in the state entry for when you bonded. +You then divide the entitled fees by $(\prod_0^{begin\ bonding\ period}1 + x)$ to normalize it to being the amount of rewards you're entitled to from 1 stake at that block to now. +Then as before, you multiply by the amount of stake you had initially bonded. +TODO: (Does the difference equating to that make sense, or should it be shown explicitly) + +Note that the inflation function could vary per block, +and per validator if ever a need rose. +If the inflation rate is the same for everyone then there can be a single global store for the entries corresponding to the product of inflations. +Inflation creation can trivially be epoched as long as inflation isn't required within the epoch, through changes to the $inflation$ function. + +Again note that this process is extremely efficient. + +\subsection{Withdrawing with no iteration over slashes} +TODO: Fill this out. +Core idea: you use the same mechanism as previously, but you just make that blocks $x_j$ term negative. +(So a $20\%$ slash would be equivalent to an inflation on that validator of $-20\%$) +This foregoes the constant inflation per validator, may or may not be worth it depending on expected number of slashes + +\subsection{Auto bonding fees} +TODO: Fill this out. +Core idea: you use the same mechanism as previously, but you just don't take that optimization with $n_{i}$ and the $n_{start}$ relation. +Fairly simple to do. \subsection{Delegation updates} -Updating your delegation amount is equivalent to withdrawing earned rewards and a fully independent new delegation occuring in the same block. +Updating your delegation amount is equivalent to withdrawing earned rewards and a fully independent new delegation occurring in the same block. +The same applies for redelegation. +From the view of fee distribution, partial redelegation is the same as a delegation update + a new delegation. \subsection{Jailing / being kicked out of the validator set} -This basically requires no change. In each block you only iterate over the currently bonded validators. So you simply don't update the "total accrued fees this period" variable for jailed / non-bonded validators. Withdrawing requires \textit{no} special casing here! +This basically requires no change. +In each block you only iterate over the currently bonded validators. +So you simply don't update the "total accrued fees this period" variable for jailed / non-bonded validators. +Withdrawing requires \textit{no} special casing here! \section{State pruning} You will notice that in the main scheme there was no note for pruning entries from state. @@ -110,11 +203,20 @@ We can in fact prune quite effectively. Suppose for the sake of exposition that there is at most one delegation / withdrawal to a particular validator in any given block. Then each delegation is responsible for one addition to state. Only the next period, and this delegator's withdrawal could depend on this entry. Thus once this delegator withdraws, this state entry can be pruned. -For the entry created by the delegator's withdrawal, that is only required by the creation of the next period. Thus once the next period is created, that withdrawal's period can be deleted. +For the entry created by the delegator's withdrawal, that is only required by the creation of the next period. +Thus once the next period is created, that withdrawal's period can be deleted. -This can be easily adapted to the case where there are multiple delegations / withdrawals per block. Keep a counter per state entry for how many delegations need to be cleared. (So 1 for each delegation in that block which created that period, 0 for each withdrawal) When creating a new period, check that the previous period (which had to be read anyway) doesn't have a count of 0. If it does have a count of 0, delete it. When withdrawing, decrement the period which created this delegation's counter by 1. If that counter is now 0, delete that period. +This can be easily adapted to the case where there are multiple delegations / withdrawals per block. +Keep a counter per state entry for how many delegations need to be cleared. +(So 1 for each delegation in that block which created that period, 0 for each withdrawal) +When creating a new period, check that the previous period (which had to be read anyway) doesn't have a count of 0. +If it does have a count of 0, delete it. +When withdrawing, decrement the period which created this delegation's counter by 1. +If that counter is now 0, delete that period. -The slash entries for a validator can only be pruned when all of that validator's delegators have their bonding period starting after the slash. This seems ineffective to keep track of, thus it is not worth it. Each slash should instead remain in state until the validator unbonds and all delegators have their fees withdrawn. +The slash entries for a validator can only be pruned when all of that validator's delegators have their bonding period starting after the slash. +This seems ineffective to keep track of, thus it is not worth it. +Each slash should instead remain in state until the validator unbonds and all delegators have their fees withdrawn. \section{Implementers Considerations} @@ -132,4 +234,16 @@ This is an extremely simple scheme with many nice benefits. Thus this scheme has efficiency improvements, simplicity improvements, and expressiveness improvements over the currently proposed schemes. With a correct fee distribution amongst the validator set, this solves the existing problem where one could withhold their signature for risk-free gain. +\section{TO DOs} + +\begin{itemize} + \item A global fee pool can be described. + \item Determine if auto-bonding fees is compatible with inflation and comission rates in conjunction + \item mention storage optimization in the uniform inflation and iteration over slashing case: only have one storage location w/ refcount of the product of inflation results + \item Remove iteration over slashes by the same normalization trick used in inflation + \item Add equation numbers + \item perhaps re-organize so that the no iteration +\end{itemize} + + \end{document} From f5e0379ef5016e1d8825712a35aac2e0f2567fe2 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 12 Dec 2018 12:17:15 +0000 Subject: [PATCH 27/27] Merge PR #3087: Fix gaia lite docs on REST server's SSL flags * Fix gaia lite docs on REST server's SSL flags Closes: #3086 * Fix rest-server command call Advanced was removed long ago --- docs/clients/lite/getting_started.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/clients/lite/getting_started.md b/docs/clients/lite/getting_started.md index 15bc8a279..faa3ea91b 100644 --- a/docs/clients/lite/getting_started.md +++ b/docs/clients/lite/getting_started.md @@ -13,7 +13,7 @@ To start a REST server, we need to specify the following parameters: For example:: ```bash -gaiacli advanced rest-server --chain-id=test \ +gaiacli rest-server --chain-id=test \ --laddr=tcp://localhost:1317 \ --node tcp://localhost:26657 \ --trust-node=false @@ -22,11 +22,11 @@ gaiacli advanced rest-server --chain-id=test \ The server listens on HTTPS by default. You can set the SSL certificate to be used by the server with these additional flags: ```bash -gaiacli advanced rest-server --chain-id=test \ +gaiacli rest-server --chain-id=test \ --laddr=tcp://localhost:1317 \ --node tcp://localhost:26657 \ --trust-node=false \ - --certfile=mycert.pem --keyfile=mykey.key + --ssl-certfile=mycert.pem --ssl-keyfile=mykey.key ``` If no certificate/keyfile pair is supplied, a self-signed certificate will be generated and its fingerprint printed out.