diff --git a/PENDING.md b/PENDING.md index 9faae7c63..1d4b41e58 100644 --- a/PENDING.md +++ b/PENDING.md @@ -19,8 +19,10 @@ BREAKING CHANGES FEATURES * Gaia REST API (`gaiacli advanced rest-server`) + * \#2399 Implement `/slashing/parameters` endpoint to query slashing parameters. * Gaia CLI (`gaiacli`) + * \#2399 Implement `params` command to query slashing parameters. * Gaia diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 164edb0f3..64767be1c 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -26,6 +26,7 @@ import ( "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" ) @@ -741,3 +742,15 @@ 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()) } + +func TestSlashingGetParams(t *testing.T) { + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + res, body := Request(t, port, "GET", "/slashing/parameters", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var params slashing.Params + err := cdc.UnmarshalJSON([]byte(body), ¶ms) + require.NoError(t, err) +} diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 8a14cb77f..417b25b87 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -1187,6 +1187,35 @@ paths: description: Key password is wrong 500: description: Internal Server Error + /slashing/parameters: + get: + summary: Get the current slashing parameters + tags: + - ICS23 + produces: + - application/json + responses: + 200: + description: OK + schema: + type: object + properties: + max_evidence_age: + type: integer + signed_blocks_window: + type: integer + min_signed_per_window: + type: integer + double_sign_unbond_duration: + type: integer + downtime_unbond_duration: + type: integer + slash_fraction_double_sign: + type: integer + slash_fraction_downtime: + type: integer + 500: + description: Internal Server Error /gov/proposals: post: summary: Submit a proposal diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index d97d25f0a..ebcdc8820 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -155,6 +155,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.QueryRouter(). AddRoute("gov", gov.NewQuerier(app.govKeeper)). + AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)). AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc)) // initialize BaseApp diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 845edc757..a297356e0 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -9,6 +9,8 @@ import ( "path/filepath" "testing" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/types" @@ -676,6 +678,37 @@ func TestGaiadCollectGentxs(t *testing.T) { cleanupDirs(gaiadHome, gaiacliHome, gentxDir) } +// --------------------------------------------------------------------------- +// Slashing + +func TestSlashingGetParams(t *testing.T) { + t.Parallel() + + cdc := app.MakeCodec() + chainID, servAddr, port, gaiadHome, gaiacliHome, p2pAddr := initializeFixtures(t) + flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) + + // start gaiad server + proc := tests.GoExecuteTWithStdout( + t, + fmt.Sprintf( + "gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", + gaiadHome, servAddr, p2pAddr, + ), + ) + + defer proc.Stop(false) + tests.WaitForTMStart(port) + tests.WaitForNextNBlocksTM(1, port) + + res, errStr := tests.ExecuteT(t, fmt.Sprintf("gaiacli query slashing params %s", flags), "") + require.Empty(t, errStr) + + var params slashing.Params + err := cdc.UnmarshalJSON([]byte(res), ¶ms) + require.NoError(t, err) +} + //___________________________________________________________________________________ // helper methods diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 41b1ca5e1..ba2e8c8c0 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -236,6 +236,32 @@ You can also query a single transaction by its hash using the following command: gaiacli query tx [hash] ``` +### Slashing + +#### Unjailing + +To unjail your jailed validator + +```bash +gaiacli tx slashing unjail --from +``` + +#### Signing Info + +To retrieve a validator's signing info: + +```bash +gaiacli query slashing signing-info +``` + +#### Query Parameters + +You can get the current slashing parameters via: + +```bash +gaiacli query slashing params +``` + ### Staking #### Set up a Validator diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index de4fc5d57..e174567dd 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -57,3 +57,25 @@ func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } + +// GetCmdQueryParams implements a command to fetch slashing parameters. +func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current slashing parameters", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + route := fmt.Sprintf("custom/%s/parameters", slashing.QuerierRoute) + + res, err := cliCtx.QueryWithData(route, nil) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} diff --git a/x/slashing/client/module_client.go b/x/slashing/client/module_client.go index 2d7c6b6eb..0ce05df64 100644 --- a/x/slashing/client/module_client.go +++ b/x/slashing/client/module_client.go @@ -26,8 +26,12 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command { Short: "Querying commands for the slashing module", } - slashingQueryCmd.AddCommand(client.GetCommands( - cli.GetCmdQuerySigningInfo(mc.storeKey, mc.cdc))...) + slashingQueryCmd.AddCommand( + client.GetCommands( + cli.GetCmdQuerySigningInfo(mc.storeKey, mc.cdc), + cli.GetCmdQueryParams(mc.cdc), + )..., + ) return slashingQueryCmd diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index a95c01acf..fd047cae0 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -1,6 +1,7 @@ package rest import ( + "fmt" "net/http" "github.com/gorilla/mux" @@ -17,6 +18,11 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co "/slashing/validators/{validatorPubKey}/signing_info", signingInfoHandlerFn(cliCtx, "slashing", cdc), ).Methods("GET") + + r.HandleFunc( + "/slashing/parameters", + queryParamsHandlerFn(cdc, cliCtx), + ).Methods("GET") } // http request handler to query signing info @@ -55,3 +61,17 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code utils.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent) } } + +func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + route := fmt.Sprintf("custom/%s/parameters", slashing.QuerierRoute) + + res, err := cliCtx.QueryWithData(route, nil) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} diff --git a/x/slashing/params.go b/x/slashing/params.go index 54d8f7f12..ad12e7344 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -30,13 +30,13 @@ func ParamTypeTable() params.TypeTable { // Params - used for initializing default parameter for slashing at genesis type Params struct { - MaxEvidenceAge time.Duration `json:"max-evidence-age"` - SignedBlocksWindow int64 `json:"signed-blocks-window"` - MinSignedPerWindow sdk.Dec `json:"min-signed-per-window"` - DoubleSignUnbondDuration time.Duration `json:"double-sign-unbond-duration"` - DowntimeUnbondDuration time.Duration `json:"downtime-unbond-duration"` - SlashFractionDoubleSign sdk.Dec `json:"slash-fraction-double-sign"` - SlashFractionDowntime sdk.Dec `json:"slash-fraction-downtime"` + MaxEvidenceAge time.Duration `json:"max_evidence_age"` + SignedBlocksWindow int64 `json:"signed_blocks_window"` + MinSignedPerWindow sdk.Dec `json:"min_signed_per_window"` + DoubleSignUnbondDuration time.Duration `json:"double_sign_unbond_duration"` + DowntimeUnbondDuration time.Duration `json:"downtime_unbond_duration"` + SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign"` + SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime"` } // Implements params.ParamStruct @@ -89,7 +89,7 @@ func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) { return } -// Downtime slashing thershold - default 50% of the SignedBlocksWindow +// Downtime slashing threshold - default 50% of the SignedBlocksWindow func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { var minSignedPerWindow sdk.Dec k.paramspace.Get(ctx, KeyMinSignedPerWindow, &minSignedPerWindow) @@ -120,3 +120,9 @@ func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) { k.paramspace.Get(ctx, KeySlashFractionDowntime, &res) return } + +// GetParams returns the total set of slashing parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params Params) { + k.paramspace.GetParamSet(ctx, ¶ms) + return params +} diff --git a/x/slashing/querier.go b/x/slashing/querier.go new file mode 100644 index 000000000..50adb486d --- /dev/null +++ b/x/slashing/querier.go @@ -0,0 +1,38 @@ +package slashing + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Query endpoints supported by the slashing querier +const ( + QuerierRoute = "slashing" + + QueryParameters = "parameters" +) + +// NewQuerier creates a new querier for slashing clients. +func NewQuerier(k Keeper, cdc *codec.Codec) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + switch path[0] { + case QueryParameters: + return queryParams(ctx, cdc, k) + default: + return nil, sdk.ErrUnknownRequest("unknown stake query endpoint") + } + } +} + +func queryParams(ctx sdk.Context, cdc *codec.Codec, k Keeper) ([]byte, sdk.Error) { + params := k.GetParams(ctx) + + res, err := codec.MarshalJSONIndent(cdc, params) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal JSON", err.Error())) + } + + return res, nil +} diff --git a/x/slashing/querier_test.go b/x/slashing/querier_test.go new file mode 100644 index 000000000..bc1ba804b --- /dev/null +++ b/x/slashing/querier_test.go @@ -0,0 +1,38 @@ +package slashing + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" +) + +func TestNewQuerier(t *testing.T) { + cdc := codec.New() + ctx, _, _, _, keeper := createTestInput(t, keeperTestParams()) + querier := NewQuerier(keeper, cdc) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + _, err := querier(ctx, []string{"parameters"}, query) + require.NoError(t, err) +} + +func TestQueryParams(t *testing.T) { + cdc := codec.New() + ctx, _, _, _, keeper := createTestInput(t, keeperTestParams()) + + var params Params + + res, errRes := queryParams(ctx, cdc, keeper) + require.NoError(t, errRes) + + err := cdc.UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + require.Equal(t, keeper.GetParams(ctx), params) +}