diff --git a/CHANGELOG.md b/CHANGELOG.md index 36ba7395..6b996d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +[\#591](https://github.com/Kava-Labs/kava/pull/591) Add a `raw-params` cli method to query raw parameter values for use in manual verification of gov proposals. + [\#596](https://github.com/Kava-Labs/kava/pull/596) Add REST client and CLI query to get module account information for the CDP module [\#590](https://github.com/Kava-Labs/kava/pull/590) Add CLI query to return kavadist module account balance diff --git a/x/committee/client/cli/query.go b/x/committee/client/cli/query.go index 68928e11..8bda3128 100644 --- a/x/committee/client/cli/query.go +++ b/x/committee/client/cli/query.go @@ -37,7 +37,8 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { GetCmdQueryVotes(queryRoute, cdc), // other GetCmdQueryProposer(queryRoute, cdc), - GetCmdQueryTally(queryRoute, cdc))...) + GetCmdQueryTally(queryRoute, cdc), + GetCmdQueryRawParams(queryRoute, cdc))...) return queryCmd } @@ -292,3 +293,37 @@ func GetCmdQueryProposer(queryRoute string, cdc *codec.Codec) *cobra.Command { }, } } + +func GetCmdQueryRawParams(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "raw-params [subspace] [key]", + Args: cobra.ExactArgs(2), + Short: "Query raw parameter values from any module.", + Long: "Query the byte value of any module's parameters. Useful in debugging and verifying governance proposals.", + Example: fmt.Sprintf("%s query %s raw-params cdp CollateralParams", version.ClientName, types.ModuleName), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + // Prepare params for querier + bz, err := cdc.MarshalJSON(types.NewQueryRawParamsParams(args[0], args[1])) + if err != nil { + return err + } + + // Query + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRawParams), bz) + if err != nil { + return err + } + + // Decode and print results + rawParams := []byte{} + err = cdc.UnmarshalJSON(res, &rawParams) + if err != nil { + return err + } + // raw params are encoded raw json bytes, so should be ok to print out as a string + return cliCtx.PrintOutput(string(rawParams)) + }, + } +} diff --git a/x/committee/keeper/querier.go b/x/committee/keeper/querier.go index ca4ce994..a098854b 100644 --- a/x/committee/keeper/querier.go +++ b/x/committee/keeper/querier.go @@ -29,6 +29,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryVote(ctx, path[1:], req, keeper) case types.QueryTally: return queryTally(ctx, path[1:], req, keeper) + case types.QueryRawParams: + return queryRawParams(ctx, path[1:], req, keeper) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) @@ -172,3 +174,28 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } return bz, nil } + +// ------------------------------------------ +// Raw Params +// ------------------------------------------ + +func queryRawParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { + var params types.QueryRawParamsParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + subspace, found := keeper.ParamKeeper.GetSubspace(params.Subspace) + if !found { + return nil, sdkerrors.Wrapf(types.ErrUnknownSubspace, "subspace: %s", params.Subspace) + } + rawParams := subspace.GetRaw(ctx, []byte(params.Key)) + + // encode the raw params as json, which converts them to a base64 string + bz, err := codec.MarshalJSONIndent(keeper.cdc, rawParams) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + return bz, nil +} diff --git a/x/committee/keeper/querier_test.go b/x/committee/keeper/querier_test.go index 762bf605..9c13fda1 100644 --- a/x/committee/keeper/querier_test.go +++ b/x/committee/keeper/querier_test.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/params" abci "github.com/tendermint/tendermint/abci/types" @@ -238,6 +239,57 @@ func (suite *QuerierTestSuite) TestQueryTally() { // Check suite.Equal(int64(len(suite.votes[propID])), tally) } + +type TestSubParam struct { + Some string + Test sdk.Dec + Params []types.Vote +} +type TestParams struct { + TestKey TestSubParam +} + +const paramKey = "TestKey" + +func (p *TestParams) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ + params.NewParamSetPair([]byte(paramKey), &p.TestKey, func(interface{}) error { return nil }), + } +} +func (suite *QuerierTestSuite) TestQueryRawParams() { + ctx := suite.ctx.WithIsCheckTx(false) // ? + + // Create a new param subspace to avoid adding dependency to another module. Set a test param value. + subspaceName := "test" + subspace := suite.app.GetParamsKeeper().Subspace(subspaceName) + subspace = subspace.WithKeyTable(params.NewKeyTable().RegisterParamSet(&TestParams{})) + + paramValue := TestSubParam{ + Some: "test", + Test: d("1000000000000.000000000000000001"), + Params: []types.Vote{{1, suite.addresses[0]}, {12, suite.addresses[1]}}, + } + subspace.Set(ctx, []byte(paramKey), paramValue) + + // Set up request query + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryRawParams}, "/"), + Data: suite.cdc.MustMarshalJSON(types.NewQueryRawParamsParams(subspaceName, paramKey)), + } + + // Execute query + bz, err := suite.querier(ctx, []string{types.QueryRawParams}, query) + suite.NoError(err) + suite.NotNil(bz) + + // Unmarshal the bytes + var returnedParamValue []byte + suite.NoError(suite.cdc.UnmarshalJSON(bz, &returnedParamValue)) + + // Check + suite.Equal(suite.cdc.MustMarshalJSON(paramValue), returnedParamValue) +} + func TestQuerierTestSuite(t *testing.T) { suite.Run(t, new(QuerierTestSuite)) } diff --git a/x/committee/types/errors.go b/x/committee/types/errors.go index f5a6fc49..e95779c0 100644 --- a/x/committee/types/errors.go +++ b/x/committee/types/errors.go @@ -13,4 +13,5 @@ var ( ErrUnknownVote = sdkerrors.Register(ModuleName, 7, "vote not found") ErrInvalidGenesis = sdkerrors.Register(ModuleName, 8, "invalid genesis") ErrNoProposalHandlerExists = sdkerrors.Register(ModuleName, 9, "pubproposal has no corresponding handler") + ErrUnknownSubspace = sdkerrors.Register(ModuleName, 10, "subspace not found") ) diff --git a/x/committee/types/querier.go b/x/committee/types/querier.go index af1df4f2..5512323f 100644 --- a/x/committee/types/querier.go +++ b/x/committee/types/querier.go @@ -13,6 +13,7 @@ const ( QueryVotes = "votes" QueryVote = "vote" QueryTally = "tally" + QueryRawParams = "raw_params" ) type QueryCommitteeParams struct { @@ -46,3 +47,15 @@ func NewQueryVoteParams(proposalID uint64, voter sdk.AccAddress) QueryVoteParams Voter: voter, } } + +type QueryRawParamsParams struct { + Subspace string + Key string +} + +func NewQueryRawParamsParams(subspace, key string) QueryRawParamsParams { + return QueryRawParamsParams{ + Subspace: subspace, + Key: key, + } +}