From cfb5acca6eff494364bdd6190c5e8a1ddbd9cb07 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 21 Aug 2018 17:32:54 +0200 Subject: [PATCH] Merge PR #2099: Query staking Pool and Params --- PENDING.md | 3 +- client/lcd/lcd_test.go | 36 +++++++++++++ cmd/gaia/cli_test/cli_test.go | 39 ++++++++++++++ cmd/gaia/cmd/gaiacli/main.go | 2 + docs/sdk/clients.md | 35 ++++++++++++ examples/basecoin/cmd/basecli/main.go | 10 ++++ x/stake/client/cli/query.go | 78 +++++++++++++++++++++++++++ x/stake/client/rest/query.go | 72 +++++++++++++++++++++++++ x/stake/types/params.go | 35 ++++++++++++ x/stake/types/pool.go | 37 ++++++++++++- 10 files changed, 345 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index c455419c98..7f297c0f37 100644 --- a/PENDING.md +++ b/PENDING.md @@ -23,8 +23,10 @@ BREAKING CHANGES FEATURES * Gaia REST API (`gaiacli advanced rest-server`) + * [lcd] Endpoints to query staking pool and params * Gaia CLI (`gaiacli`) + * [cli] Cmds to query staking pool and params * Gaia @@ -63,4 +65,3 @@ BUG FIXES * \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988) * Tendermint - diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 09c0eddd11..fbbf6a6cff 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -353,6 +353,42 @@ func TestTxs(t *testing.T) { require.Equal(t, resultTx.Height, indexedTxs[0].Height) } +func TestPoolParamsQuery(t *testing.T) { + _, password := "test", "1234567890" + addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + 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) + 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) + + 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.DateLastCommissionReset, pool.DateLastCommissionReset) + require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares) + require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) + require.Equal(t, initialPool.NextInflation(params), pool.Inflation) + initialPool = initialPool.ProcessProvisions(params) // provisions are added to the pool every hour + require.Equal(t, initialPool.LooseTokens, pool.LooseTokens) +} + func TestValidatorsQuery(t *testing.T) { cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 4f041d8485..47a1949dd2 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -117,6 +117,11 @@ func TestGaiaCLICreateValidator(t *testing.T) { fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) + defaultParams := stake.DefaultParams() + initialPool := stake.InitialPool() + initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState + initialPool = initialPool.ProcessProvisions(defaultParams) // provisions are added to the pool every hour + // create validator cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags) cvStr += fmt.Sprintf(" --from=%s", "bar") @@ -124,6 +129,8 @@ func TestGaiaCLICreateValidator(t *testing.T) { cvStr += fmt.Sprintf(" --amount=%v", "2steak") cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally") + initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1)) + executeWrite(t, cvStr, app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) @@ -150,6 +157,14 @@ func TestGaiaCLICreateValidator(t *testing.T) { */ validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags)) require.Equal(t, "1.0000000000", validator.Tokens.String()) + + params := executeGetParams(t, fmt.Sprintf("gaiacli stake parameters --output=json %v", flags)) + require.True(t, defaultParams.Equal(params)) + + pool := executeGetPool(t, fmt.Sprintf("gaiacli stake pool --output=json %v", flags)) + require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset) + require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares) + require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) } func TestGaiaCLISubmitProposal(t *testing.T) { @@ -328,6 +343,9 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { return acc } +//___________________________________________________________________________________ +// stake + func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { out := tests.ExecuteT(t, cmdStr, "") var validator stake.Validator @@ -337,6 +355,27 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { return validator } +func executeGetPool(t *testing.T, cmdStr string) stake.Pool { + out := tests.ExecuteT(t, cmdStr, "") + var pool stake.Pool + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &pool) + require.NoError(t, err, "out %v\n, err %v", out, err) + return pool +} + +func executeGetParams(t *testing.T, cmdStr string) stake.Params { + out := tests.ExecuteT(t, cmdStr, "") + var params stake.Params + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), ¶ms) + require.NoError(t, err, "out %v\n, err %v", out, err) + return params +} + +//___________________________________________________________________________________ +// gov + func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal { out := tests.ExecuteT(t, cmdStr, "") var proposal gov.Proposal diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 02a96b6149..d447504af9 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -88,6 +88,8 @@ func main() { stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), + stakecmd.GetCmdQueryParams("stake", cdc), + stakecmd.GetCmdQueryPool("stake", cdc), stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc), stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc), stakecmd.GetCmdQueryRedelegation("stake", cdc), diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 2432cbc0dd..be42a46dba 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -341,6 +341,41 @@ gaiacli gov query-vote \ --voter= ``` +#### Query Parameters + +You can get the current parameters that define high level settings for staking: + +``` +gaiacli stake parameters +``` + +With the above command you will get the values for: + +- Maximum and minumum Inflation rate +- Maximum annual change in inflation rate, +- Goal of bonded tokens (%) +- Unbonding time +- Maximum numbers of validators +- Coin denomination for staking + +All this values can be updated though a `governance` process by submitting a parameter change `proposal`. + +#### Query Pool + +A staking `Pool` defines the dynamic parameters of the current state. You can query them with the following command: + +``` +gaiacli stake pool +``` + +With the `pool` command you will get the values for: + +- Loose and bonded tokens +- Token supply +- Current anual inflation and the block in which the last inflation was processed +- Last recorded bonded shares + + ## Gaia-Lite ::: tip Note diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 82a2835157..15bfd8035b 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -14,6 +14,7 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" @@ -51,6 +52,13 @@ func main() { stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), + stakecmd.GetCmdQueryPool("stake", cdc), + stakecmd.GetCmdQueryParams("stake", cdc), + stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc), + stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc), + stakecmd.GetCmdQueryRedelegation("stake", cdc), + stakecmd.GetCmdQueryRedelegations("stake", cdc), + slashingcmd.GetCmdQuerySigningInfo("slashing", cdc), authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), )...) @@ -63,6 +71,8 @@ func main() { stakecmd.GetCmdEditValidator(cdc), stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond("stake", cdc), + stakecmd.GetCmdRedelegate("stake", cdc), + slashingcmd.GetCmdUnrevoke(cdc), )...) // add proxy, version and key info diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index bdf4bb198d..4b8e99e7d2 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -417,3 +417,81 @@ func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command return cmd } + +// GetCmdQueryPool implements the pool query command. +func GetCmdQueryPool(storeName string, cdc *wire.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "pool", + Short: "Query the current staking pool values", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + key := stake.PoolKey + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryStore(key, storeName) + if err != nil { + return err + } + + pool := types.MustUnmarshalPool(cdc, res) + + switch viper.Get(cli.OutputFlag) { + case "text": + human := pool.HumanReadableString() + + fmt.Println(human) + + case "json": + // parse out the pool + output, err := wire.MarshalJSONIndent(cdc, pool) + if err != nil { + return err + } + + fmt.Println(string(output)) + } + return nil + }, + } + + return cmd +} + +// GetCmdQueryPool implements the params query command. +func GetCmdQueryParams(storeName string, cdc *wire.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "parameters", + Short: "Query the current staking parameters information", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + key := stake.ParamKey + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryStore(key, storeName) + if err != nil { + return err + } + + params := types.MustUnmarshalParams(cdc, res) + + switch viper.Get(cli.OutputFlag) { + case "text": + human := params.HumanReadableString() + + fmt.Println(human) + + case "json": + // parse out the params + output, err := wire.MarshalJSONIndent(cdc, params) + if err != nil { + return err + } + + fmt.Println(string(output)) + } + return nil + }, + } + + return cmd +} diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 036a3b4511..a040c7541c 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -68,6 +68,18 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Cod validatorHandlerFn(cliCtx, cdc), ).Methods("GET") + // Get the current state of the staking pool + r.HandleFunc( + "/stake/pool", + poolHandlerFn(cliCtx, cdc), + ).Methods("GET") + + // Get the current staking parameter values + r.HandleFunc( + "/stake/parameters", + paramsHandlerFn(cliCtx, cdc), + ).Methods("GET") + } // already resolve the rational shares to not handle this in the client @@ -554,3 +566,63 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handler w.Write(output) } } + +// HTTP request handler to query the pool information +func poolHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + key := stake.PoolKey + + res, err := cliCtx.QueryStore(key, storeName) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't query pool. Error: %s", err.Error()))) + return + } + + pool, err := types.UnmarshalPool(cdc, res) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + output, err := cdc.MarshalJSON(pool) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + w.Write(output) + } +} + +// HTTP request handler to query the staking params values +func paramsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + key := stake.ParamKey + + res, err := cliCtx.QueryStore(key, storeName) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't query parameters. Error: %s", err.Error()))) + return + } + + params, err := types.UnmarshalParams(cdc, res) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + output, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + w.Write(output) + } +} diff --git a/x/stake/types/params.go b/x/stake/types/params.go index f297f31059..bad4251cb3 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -2,9 +2,11 @@ package types import ( "bytes" + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) // defaultUnbondingTime reflects three weeks in seconds as the default @@ -43,3 +45,36 @@ func DefaultParams() Params { BondDenom: "steak", } } + +// HumanReadableString returns a human readable string representation of the +// parameters. +func (p Params) HumanReadableString() string { + + resp := "Pool \n" + resp += fmt.Sprintf("Maximum Annual Inflation Rate Change: %s\n", p.InflationRateChange) + resp += fmt.Sprintf("Max Inflation Rate: %s\n", p.InflationMax) + resp += fmt.Sprintf("Min Inflation Tate: %s\n", p.InflationMin) + resp += fmt.Sprintf("Bonded Token Goal (%s): %s\n", "s", p.GoalBonded) + resp += fmt.Sprintf("Unbonding Time: %s\n", p.UnbondingTime) + resp += fmt.Sprintf("Max Validators: %d: \n", p.MaxValidators) + resp += fmt.Sprintf("Bonded Coin Denomination: %s\n", p.BondDenom) + return resp +} + +// unmarshal the current staking params value from store key or panic +func MustUnmarshalParams(cdc *wire.Codec, value []byte) Params { + params, err := UnmarshalParams(cdc, value) + if err != nil { + panic(err) + } + return params +} + +// unmarshal the current staking params value from store key +func UnmarshalParams(cdc *wire.Codec, value []byte) (params Params, err error) { + err = cdc.UnmarshalBinary(value, ¶ms) + if err != nil { + return + } + return +} diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index d59c1ed251..b01ed8e9a9 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -6,13 +6,14 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) // Pool - dynamic parameters of the current state type Pool struct { LooseTokens sdk.Dec `json:"loose_tokens"` // tokens which are not bonded in a validator BondedTokens sdk.Dec `json:"bonded_tokens"` // reserve of bonded tokens - InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time + InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed Inflation sdk.Dec `json:"inflation"` // current annual inflation rate DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily) @@ -123,3 +124,37 @@ func (p Pool) NextInflation(params Params) (inflation sdk.Dec) { return inflation } + +// HumanReadableString returns a human readable string representation of a +// pool. +func (p Pool) HumanReadableString() string { + + resp := "Pool \n" + resp += fmt.Sprintf("Loose Tokens: %s\n", p.LooseTokens) + resp += fmt.Sprintf("Bonded Tokens: %s\n", p.BondedTokens) + resp += fmt.Sprintf("Token Supply: %s\n", p.TokenSupply()) + resp += fmt.Sprintf("Bonded Ratio: %v\n", p.BondedRatio()) + resp += fmt.Sprintf("Previous Inflation Block: %s\n", p.InflationLastTime) + resp += fmt.Sprintf("Inflation: %v\n", p.Inflation) + resp += fmt.Sprintf("Date of Last Commission Reset: %d\n", p.DateLastCommissionReset) + resp += fmt.Sprintf("Previous Bonded Shares: %v\n", p.PrevBondedShares) + return resp +} + +// unmarshal the current pool value from store key or panics +func MustUnmarshalPool(cdc *wire.Codec, value []byte) Pool { + pool, err := UnmarshalPool(cdc, value) + if err != nil { + panic(err) + } + return pool +} + +// unmarshal the current pool value from store key +func UnmarshalPool(cdc *wire.Codec, value []byte) (pool Pool, err error) { + err = cdc.UnmarshalBinary(value, &pool) + if err != nil { + return + } + return +}