From 44f972584d404edb76249905af9a0073a534d83a Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 15 May 2019 10:44:47 -0400 Subject: [PATCH] Merge PR #4333: Remove Shares from Client Responses --- .../bugfixes/gaiacli/4190-Fix-redelegatio | 1 + .../improvements/sdk/4190-Client-response | 2 + client/lcd/swagger-ui/swagger.yaml | 11 +- cmd/gaia/lcd_test/helpers_test.go | 15 +- x/staking/alias.go | 14 +- x/staking/client/cli/query.go | 114 +++++++++----- x/staking/client/rest/query.go | 6 +- x/staking/keeper/query_utils.go | 10 +- x/staking/querier/querier.go | 67 ++++++-- x/staking/querier/querier_test.go | 52 ++++--- x/staking/querier/utils.go | 71 +++++++++ x/staking/types/delegation.go | 143 +++++++++++++++++- x/staking/types/delegation_test.go | 64 ++++++++ 13 files changed, 470 insertions(+), 100 deletions(-) create mode 100644 .pending/bugfixes/gaiacli/4190-Fix-redelegatio create mode 100644 .pending/improvements/sdk/4190-Client-response create mode 100644 x/staking/querier/utils.go diff --git a/.pending/bugfixes/gaiacli/4190-Fix-redelegatio b/.pending/bugfixes/gaiacli/4190-Fix-redelegatio new file mode 100644 index 0000000000..c9106a0382 --- /dev/null +++ b/.pending/bugfixes/gaiacli/4190-Fix-redelegatio @@ -0,0 +1 @@ +#4190 Fix redelegations-from by using the correct params and query endpoint. diff --git a/.pending/improvements/sdk/4190-Client-response b/.pending/improvements/sdk/4190-Client-response new file mode 100644 index 0000000000..b257c82fe5 --- /dev/null +++ b/.pending/improvements/sdk/4190-Client-response @@ -0,0 +1,2 @@ +#4190 Client responses that return (re)delegation(s) now return balances +instead of shares. diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 7d1afede22..7f3fe1e509 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -2381,16 +2381,21 @@ definitions: type: string validator_dst_address: type: string + entries: + type: array + items: + $ref: "#/definitions/Redelegation" + RedelegationEntry: + type: object + properties: creation_height: type: integer - min_time: + completion_time: type: integer initial_balance: type: string balance: type: string - shares_src: - type: string shares_dst: type: string ValidatorDistInfo: diff --git a/cmd/gaia/lcd_test/helpers_test.go b/cmd/gaia/lcd_test/helpers_test.go index 51deefa52a..4573dcaf45 100644 --- a/cmd/gaia/lcd_test/helpers_test.go +++ b/cmd/gaia/lcd_test/helpers_test.go @@ -929,11 +929,11 @@ func doBeginRedelegation( } // GET /staking/delegators/{delegatorAddr}/delegations Get all delegations from a delegator -func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) []staking.Delegation { +func getDelegatorDelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress) staking.DelegationResponses { res, body := Request(t, port, "GET", fmt.Sprintf("/staking/delegators/%s/delegations", delegatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var dels []staking.Delegation + var dels staking.DelegationResponses err := cdc.UnmarshalJSON([]byte(body), &dels) require.Nil(t, err) @@ -955,7 +955,7 @@ func getDelegatorUnbondingDelegations(t *testing.T, port string, delegatorAddr s } // GET /staking/redelegations?delegator=0xdeadbeef&validator_from=0xdeadbeef&validator_to=0xdeadbeef& Get redelegations filters by params passed in -func getRedelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress, srcValidatorAddr sdk.ValAddress, dstValidatorAddr sdk.ValAddress) []staking.Redelegation { +func getRedelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress, srcValidatorAddr sdk.ValAddress, dstValidatorAddr sdk.ValAddress) staking.RedelegationResponses { var res *http.Response var body string endpoint := "/staking/redelegations?" @@ -968,11 +968,14 @@ func getRedelegations(t *testing.T, port string, delegatorAddr sdk.AccAddress, s if !dstValidatorAddr.Empty() { endpoint += fmt.Sprintf("validator_to=%s&", dstValidatorAddr) } + res, body = Request(t, port, "GET", endpoint, nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var redels []staking.Redelegation + + var redels staking.RedelegationResponses err := cdc.UnmarshalJSON([]byte(body), &redels) require.Nil(t, err) + return redels } @@ -1022,11 +1025,11 @@ func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, quer } // GET /staking/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) staking.Delegation { +func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) staking.DelegationResp { res, body := Request(t, port, "GET", fmt.Sprintf("/staking/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var bond staking.Delegation + var bond staking.DelegationResp err := cdc.UnmarshalJSON([]byte(body), &bond) require.Nil(t, err) diff --git a/x/staking/alias.go b/x/staking/alias.go index 3c8e19fd9f..893a2c81a1 100644 --- a/x/staking/alias.go +++ b/x/staking/alias.go @@ -18,11 +18,14 @@ type ( Commission = types.Commission CommissionMsg = types.CommissionMsg Delegation = types.Delegation + DelegationResp = types.DelegationResponse + DelegationResponses = types.DelegationResponses Delegations = types.Delegations UnbondingDelegation = types.UnbondingDelegation UnbondingDelegations = types.UnbondingDelegations Redelegation = types.Redelegation Redelegations = types.Redelegations + RedelegationResponses = types.RedelegationResponses Params = types.Params Pool = types.Pool MsgCreateValidator = types.MsgCreateValidator @@ -97,11 +100,12 @@ var ( NewMsgUndelegate = types.NewMsgUndelegate NewMsgBeginRedelegate = types.NewMsgBeginRedelegate - NewQuerier = querier.NewQuerier - NewQueryDelegatorParams = querier.NewQueryDelegatorParams - NewQueryValidatorParams = querier.NewQueryValidatorParams - NewQueryBondsParams = querier.NewQueryBondsParams - NewQueryValidatorsParams = querier.NewQueryValidatorsParams + NewQuerier = querier.NewQuerier + NewQueryDelegatorParams = querier.NewQueryDelegatorParams + NewQueryValidatorParams = querier.NewQueryValidatorParams + NewQueryBondsParams = querier.NewQueryBondsParams + NewQueryValidatorsParams = querier.NewQueryValidatorsParams + NewQueryRedelegationParams = querier.NewQueryRedelegationParams ) const ( diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index 7366dc5ae3..94f2fff5e6 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -109,7 +109,8 @@ $ gaiacli query staking unbonding-delegations-from cosmosvaloper1gghjut3ccd8ay0z } } -// GetCmdQueryValidatorRedelegations implements the query all redelegatations from a validator command. +// GetCmdQueryValidatorRedelegations implements the query all redelegatations +// from a validator command. func GetCmdQueryValidatorRedelegations(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "redelegations-from [validator-addr]", @@ -122,31 +123,34 @@ $ gaiacli query staking redelegations-from cosmosvaloper1gghjut3ccd8ay0zduzj64hw RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - valAddr, err := sdk.ValAddressFromBech32(args[0]) + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(valAddr)) + bz, err := cdc.MarshalJSON(staking.QueryRedelegationParams{SrcValidatorAddr: valSrcAddr}) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryValidatorRedelegations) + route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryRedelegations) res, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - var reds staking.Redelegations - cdc.MustUnmarshalJSON(res, &reds) - return cliCtx.PrintOutput(reds) + var resp staking.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } // GetCmdQueryDelegation the query delegation command. -func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryDelegation(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "delegation [delegator-addr] [validator-addr]", Short: "Query a delegation based on address and validator address", @@ -158,34 +162,40 @@ $ gaiacli query staking delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - valAddr, err := sdk.ValAddressFromBech32(args[1]) - if err != nil { - return err - } - delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - res, err := cliCtx.QueryStore(staking.GetDelegationKey(delAddr, valAddr), storeName) + valAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { return err } - delegation, err := types.UnmarshalDelegation(cdc, res) + bz, err := cdc.MarshalJSON(staking.NewQueryBondsParams(delAddr, valAddr)) if err != nil { return err } - return cliCtx.PrintOutput(delegation) + route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryDelegation) + res, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp staking.DelegationResp + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } // GetCmdQueryDelegations implements the command to query all the delegations // made from one delegator. -func GetCmdQueryDelegations(storeName string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryDelegations(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "delegations [delegator-addr]", Short: "Query all delegations made by one delegator", @@ -197,22 +207,28 @@ $ gaiacli query staking delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9 RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - resKVs, err := cliCtx.QuerySubspace(staking.GetDelegationsKey(delegatorAddr), storeName) + bz, err := cdc.MarshalJSON(staking.NewQueryDelegatorParams(delAddr)) if err != nil { return err } - var delegations staking.Delegations - for _, kv := range resKVs { - delegations = append(delegations, types.MustUnmarshalDelegation(cdc, kv.Value)) + route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryDelegatorDelegations) + res, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err } - return cliCtx.PrintOutput(delegations) + var resp staking.DelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } @@ -231,12 +247,12 @@ $ gaiacli query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2f RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(validatorAddr)) + bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(valAddr)) if err != nil { return err } @@ -247,9 +263,12 @@ $ gaiacli query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2f return err } - var dels staking.Delegations - cdc.MustUnmarshalJSON(res, &dels) - return cliCtx.PrintOutput(dels) + var resp staking.DelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } @@ -325,7 +344,7 @@ $ gaiacli query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fx // GetCmdQueryRedelegation implements the command to query a single // redelegation record. -func GetCmdQueryRedelegation(storeName string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryRedelegation(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "redelegation [delegator-addr] [src-validator-addr] [dst-validator-addr]", Short: "Query a redelegation record based on delegator and a source and destination validator address", @@ -337,6 +356,11 @@ $ gaiacli query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + valSrcAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { return err @@ -347,24 +371,30 @@ $ gaiacli query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru return err } - delAddr, err := sdk.AccAddressFromBech32(args[0]) + bz, err := cdc.MarshalJSON(staking.NewQueryRedelegationParams(delAddr, valSrcAddr, valDstAddr)) if err != nil { return err } - res, err := cliCtx.QueryStore(staking.GetREDKey(delAddr, valSrcAddr, valDstAddr), storeName) + route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryRedelegations) + res, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - return cliCtx.PrintOutput(types.MustUnmarshalRED(cdc, res)) + var resp staking.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } // GetCmdQueryRedelegations implements the command to query all the // redelegation records for a delegator. -func GetCmdQueryRedelegations(storeName string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryRedelegations(storeKey string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "redelegations [delegator-addr]", Args: cobra.ExactArgs(1), @@ -376,22 +406,28 @@ $ gaiacli query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - resKVs, err := cliCtx.QuerySubspace(staking.GetREDsKey(delegatorAddr), storeName) + bz, err := cdc.MarshalJSON(staking.QueryRedelegationParams{DelegatorAddr: delAddr}) if err != nil { return err } - var reds staking.Redelegations - for _, kv := range resKVs { - reds = append(reds, types.MustUnmarshalRED(cdc, kv.Value)) + route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryRedelegations) + res, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err } - return cliCtx.PrintOutput(reds) + var resp staking.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) }, } } diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index 38129101d8..19c52026c7 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -103,7 +103,7 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co // HTTP request handler to query a delegator delegations func delegatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryDelegator(cliCtx, cdc, "custom/staking/delegatorDelegations") + return queryDelegator(cliCtx, cdc, fmt.Sprintf("custom/%s/%s", staking.QuerierRoute, staking.QueryDelegatorDelegations)) } // HTTP request handler to query a delegator unbonding delegations @@ -228,7 +228,7 @@ func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Ha // HTTP request handler to query a delegation func delegationHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryBonds(cliCtx, cdc, "custom/staking/delegation") + return queryBonds(cliCtx, cdc, fmt.Sprintf("custom/%s/%s", staking.QuerierRoute, staking.QueryDelegation)) } // HTTP request handler to query all delegator bonded validators @@ -279,7 +279,7 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle // HTTP request handler to query all unbonding delegations from a validator func validatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryValidator(cliCtx, cdc, "custom/staking/validatorDelegations") + return queryValidator(cliCtx, cdc, fmt.Sprintf("custom/%s/%s", staking.QuerierRoute, staking.QueryValidatorDelegations)) } // HTTP request handler to query all unbonding delegations from a validator diff --git a/x/staking/keeper/query_utils.go b/x/staking/keeper/query_utils.go index 73d93c4e97..1c4b7efe2a 100644 --- a/x/staking/keeper/query_utils.go +++ b/x/staking/keeper/query_utils.go @@ -48,8 +48,8 @@ func (k Keeper) GetDelegatorValidator(ctx sdk.Context, delegatorAddr sdk.AccAddr //_____________________________________________________________________________________ // return all delegations for a delegator -func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) ( - delegations []types.Delegation) { +func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) []types.Delegation { + delegations := make([]types.Delegation, 0) store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegationsKey(delegator) @@ -62,12 +62,13 @@ func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAdd delegations = append(delegations, delegation) i++ } + return delegations } // return all unbonding-delegations for a delegator -func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress) ( - unbondingDelegations []types.UnbondingDelegation) { +func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress) []types.UnbondingDelegation { + unbondingDelegations := make([]types.UnbondingDelegation, 0) store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetUBDsKey(delegator) @@ -80,6 +81,7 @@ func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAdd unbondingDelegations = append(unbondingDelegations, unbondingDelegation) i++ } + return unbondingDelegations } diff --git a/x/staking/querier/querier.go b/x/staking/querier/querier.go index 8694a35b9c..24715dac4a 100644 --- a/x/staking/querier/querier.go +++ b/x/staking/querier/querier.go @@ -22,7 +22,6 @@ const ( QueryValidatorDelegations = "validatorDelegations" QueryValidatorRedelegations = "validatorRedelegations" QueryValidatorUnbondingDelegations = "validatorUnbondingDelegations" - QueryDelegator = "delegator" QueryDelegation = "delegation" QueryUnbondingDelegation = "unbondingDelegation" QueryDelegatorValidators = "delegatorValidators" @@ -37,30 +36,43 @@ func NewQuerier(k keep.Keeper, cdc *codec.Codec) sdk.Querier { switch path[0] { case QueryValidators: return queryValidators(ctx, cdc, req, k) + case QueryValidator: return queryValidator(ctx, cdc, req, k) + case QueryValidatorDelegations: return queryValidatorDelegations(ctx, cdc, req, k) + case QueryValidatorUnbondingDelegations: return queryValidatorUnbondingDelegations(ctx, cdc, req, k) + case QueryDelegation: return queryDelegation(ctx, cdc, req, k) + case QueryUnbondingDelegation: return queryUnbondingDelegation(ctx, cdc, req, k) + case QueryDelegatorDelegations: return queryDelegatorDelegations(ctx, cdc, req, k) + case QueryDelegatorUnbondingDelegations: return queryDelegatorUnbondingDelegations(ctx, cdc, req, k) + case QueryRedelegations: return queryRedelegations(ctx, cdc, req, k) + case QueryDelegatorValidators: return queryDelegatorValidators(ctx, cdc, req, k) + case QueryDelegatorValidator: return queryDelegatorValidator(ctx, cdc, req, k) + case QueryPool: return queryPool(ctx, cdc, k) + case QueryParameters: return queryParameters(ctx, cdc, k) + default: return nil, sdk.ErrUnknownRequest("unknown staking query endpoint") } @@ -202,11 +214,16 @@ func queryValidatorDelegations(ctx sdk.Context, cdc *codec.Codec, req abci.Reque } delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr) - - res, errRes = codec.MarshalJSONIndent(cdc, delegations) - if errRes != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error())) + delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + if err != nil { + return nil, err } + + res, errRes = codec.MarshalJSONIndent(cdc, delegationResps) + if errRes != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errRes.Error())) + } + return res, nil } @@ -236,11 +253,16 @@ func queryDelegatorDelegations(ctx sdk.Context, cdc *codec.Codec, req abci.Reque } delegations := k.GetAllDelegatorDelegations(ctx, params.DelegatorAddr) - - res, errRes = codec.MarshalJSONIndent(cdc, delegations) - if errRes != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error())) + delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + if err != nil { + return nil, err } + + res, errRes = codec.MarshalJSONIndent(cdc, delegationResps) + if errRes != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errRes.Error())) + } + return res, nil } @@ -313,10 +335,16 @@ func queryDelegation(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k return []byte{}, types.ErrNoDelegation(types.DefaultCodespace) } - res, errRes = codec.MarshalJSONIndent(cdc, delegation) - if errRes != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error())) + delegationResp, err := delegationToDelegationResponse(ctx, k, delegation) + if err != nil { + return nil, err } + + res, errRes = codec.MarshalJSONIndent(cdc, delegationResp) + if errRes != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errRes.Error())) + } + return res, nil } @@ -353,8 +381,9 @@ func queryRedelegations(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery if !params.DelegatorAddr.Empty() && !params.SrcValidatorAddr.Empty() && !params.DstValidatorAddr.Empty() { redel, found := k.GetRedelegation(ctx, params.DelegatorAddr, params.SrcValidatorAddr, params.DstValidatorAddr) if !found { - return []byte{}, types.ErrNoRedelegation(types.DefaultCodespace) + return nil, types.ErrNoRedelegation(types.DefaultCodespace) } + redels = []types.Redelegation{redel} } else if params.DelegatorAddr.Empty() && !params.SrcValidatorAddr.Empty() && params.DstValidatorAddr.Empty() { redels = k.GetRedelegationsFromValidator(ctx, params.SrcValidatorAddr) @@ -362,10 +391,16 @@ func queryRedelegations(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery redels = k.GetAllRedelegations(ctx, params.DelegatorAddr, params.SrcValidatorAddr, params.DstValidatorAddr) } - res, errRes = codec.MarshalJSONIndent(cdc, redels) - if errRes != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error())) + redelResponses, err := redelegationsToRedelegationResponses(ctx, k, redels) + if err != nil { + return nil, err } + + res, errRes = codec.MarshalJSONIndent(cdc, redelResponses) + if errRes != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errRes.Error())) + } + return res, nil } diff --git a/x/staking/querier/querier_test.go b/x/staking/querier/querier_test.go index f83c950706..c22b58938a 100644 --- a/x/staking/querier/querier_test.go +++ b/x/staking/querier/querier_test.go @@ -263,14 +263,15 @@ func TestQueryDelegation(t *testing.T) { res, err = queryDelegation(ctx, cdc, query, keeper) require.Nil(t, err) - var delegationRes types.Delegation + var delegationRes types.DelegationResponse errRes = cdc.UnmarshalJSON(res, &delegationRes) require.Nil(t, errRes) - require.Equal(t, delegation, delegationRes) + require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegationRes.Balance) // Query Delegator Delegations - query = abci.RequestQuery{ Path: "/custom/staking/delegatorDelegations", Data: bz, @@ -279,11 +280,13 @@ func TestQueryDelegation(t *testing.T) { res, err = queryDelegatorDelegations(ctx, cdc, query, keeper) require.Nil(t, err) - var delegatorDelegations []types.Delegation + var delegatorDelegations types.DelegationResponses errRes = cdc.UnmarshalJSON(res, &delegatorDelegations) require.Nil(t, errRes) require.Len(t, delegatorDelegations, 1) - require.Equal(t, delegation, delegatorDelegations[0]) + require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegatorDelegations[0].Balance) // error unknown request query.Data = bz[:len(bz)-1] @@ -304,11 +307,13 @@ func TestQueryDelegation(t *testing.T) { res, err = queryValidatorDelegations(ctx, cdc, query, keeper) require.Nil(t, err) - var delegationsRes []types.Delegation + var delegationsRes types.DelegationResponses errRes = cdc.UnmarshalJSON(res, &delegationsRes) require.Nil(t, errRes) - - require.Equal(t, delegationsRes[0], delegation) + require.Len(t, delegatorDelegations, 1) + require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegationsRes[0].Balance) // Query unbonging delegation unbondingTokens := sdk.TokensFromTendermintPower(10) @@ -382,11 +387,14 @@ func TestQueryDelegation(t *testing.T) { res, err = queryRedelegations(ctx, cdc, query, keeper) require.Nil(t, err) - var redelRes []types.Redelegation + var redelRes types.RedelegationResponses errRes = cdc.UnmarshalJSON(res, &redelRes) require.Nil(t, errRes) - - require.Equal(t, redel, redelRes[0]) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) } func TestQueryRedelegations(t *testing.T) { @@ -407,7 +415,7 @@ func TestQueryRedelegations(t *testing.T) { keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), rdAmount.ToDec()) keeper.ApplyAndReturnValidatorSetUpdates(ctx) - redelegation, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) + redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) require.True(t, found) // delegator redelegations @@ -423,11 +431,14 @@ func TestQueryRedelegations(t *testing.T) { res, err := queryRedelegations(ctx, cdc, query, keeper) require.Nil(t, err) - var redsRes []types.Redelegation - errRes = cdc.UnmarshalJSON(res, &redsRes) + var redelRes types.RedelegationResponses + errRes = cdc.UnmarshalJSON(res, &redelRes) require.Nil(t, errRes) - - require.Equal(t, redelegation, redsRes[0]) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) // validator redelegations queryValidatorParams := NewQueryValidatorParams(val1.GetOperator()) @@ -442,8 +453,11 @@ func TestQueryRedelegations(t *testing.T) { res, err = queryRedelegations(ctx, cdc, query, keeper) require.Nil(t, err) - errRes = cdc.UnmarshalJSON(res, &redsRes) + errRes = cdc.UnmarshalJSON(res, &redelRes) require.Nil(t, errRes) - - require.Equal(t, redelegation, redsRes[0]) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) } diff --git a/x/staking/querier/utils.go b/x/staking/querier/utils.go new file mode 100644 index 0000000000..391464af5c --- /dev/null +++ b/x/staking/querier/utils.go @@ -0,0 +1,71 @@ +package querier + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func delegationToDelegationResponse(ctx sdk.Context, k keeper.Keeper, del types.Delegation) (types.DelegationResponse, sdk.Error) { + val, found := k.GetValidator(ctx, del.ValidatorAddress) + if !found { + return types.DelegationResponse{}, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + return types.NewDelegationResp( + del.DelegatorAddress, + del.ValidatorAddress, + del.Shares, + val.TokensFromShares(del.Shares).TruncateInt(), + ), nil +} + +func delegationsToDelegationResponses( + ctx sdk.Context, k keeper.Keeper, delegations types.Delegations, +) (types.DelegationResponses, sdk.Error) { + + resp := make(types.DelegationResponses, len(delegations), len(delegations)) + for i, del := range delegations { + delResp, err := delegationToDelegationResponse(ctx, k, del) + if err != nil { + return nil, err + } + + resp[i] = delResp + } + + return resp, nil +} + +func redelegationsToRedelegationResponses( + ctx sdk.Context, k keeper.Keeper, redels types.Redelegations, +) (types.RedelegationResponses, sdk.Error) { + + resp := make(types.RedelegationResponses, len(redels), len(redels)) + for i, redel := range redels { + val, found := k.GetValidator(ctx, redel.ValidatorDstAddress) + if !found { + return nil, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + entryResponses := make([]types.RedelegationEntryResponse, len(redel.Entries), len(redel.Entries)) + for j, entry := range redel.Entries { + entryResponses[j] = types.NewRedelegationEntryResponse( + entry.CreationHeight, + entry.CompletionTime, + entry.SharesDst, + entry.InitialBalance, + val.TokensFromShares(entry.SharesDst).TruncateInt(), + ) + } + + resp[i] = types.NewRedelegationResponse( + redel.DelegatorAddress, + redel.ValidatorSrcAddress, + redel.ValidatorDstAddress, + entryResponses, + ) + } + + return resp, nil +} diff --git a/x/staking/types/delegation.go b/x/staking/types/delegation.go index 2d8647ba8e..72ceebc17e 100644 --- a/x/staking/types/delegation.go +++ b/x/staking/types/delegation.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "encoding/json" "fmt" "strings" "time" @@ -315,15 +316,22 @@ func (d Redelegation) String() string { Delegator: %s Source Validator: %s Destination Validator: %s - Entries:`, d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress) + Entries: +`, + d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress, + ) + for i, entry := range d.Entries { - out += fmt.Sprintf(` Redelegation %d: + out += fmt.Sprintf(` Redelegation Entry #%d: Creation height: %v Min time to unbond (unix): %v - Dest Shares: %s`, i, entry.CreationHeight, - entry.CompletionTime, entry.SharesDst) + Dest Shares: %s +`, + i, entry.CreationHeight, entry.CompletionTime, entry.SharesDst, + ) } - return out + + return strings.TrimRight(out, "\n") } // Redelegations are a collection of Redelegation @@ -335,3 +343,128 @@ func (d Redelegations) String() (out string) { } return strings.TrimSpace(out) } + +// ---------------------------------------------------------------------------- +// Client Types + +// DelegationResponse is equivalent to Delegation except that it contains a balance +// in addition to shares which is more suitable for client responses. +type DelegationResponse struct { + Delegation + Balance sdk.Int `json:"balance"` +} + +func NewDelegationResp(d sdk.AccAddress, v sdk.ValAddress, s sdk.Dec, b sdk.Int) DelegationResponse { + return DelegationResponse{NewDelegation(d, v, s), b} +} + +// String implements the Stringer interface for DelegationResponse. +func (d DelegationResponse) String() string { + return fmt.Sprintf("%s\n Balance: %s", d.Delegation.String(), d.Balance) +} + +type delegationRespAlias DelegationResponse + +// MarshalJSON implements the json.Marshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (d DelegationResponse) MarshalJSON() ([]byte, error) { + return json.Marshal((delegationRespAlias)(d)) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (d *DelegationResponse) UnmarshalJSON(bz []byte) error { + return json.Unmarshal(bz, (*delegationRespAlias)(d)) +} + +// DelegationResponses is a collection of DelegationResp +type DelegationResponses []DelegationResponse + +// String implements the Stringer interface for DelegationResponses. +func (d DelegationResponses) String() (out string) { + for _, del := range d { + out += del.String() + "\n" + } + return strings.TrimSpace(out) +} + +// RedelegationResponse is equivalent to a Redelegation except that its entries +// contain a balance in addition to shares which is more suitable for client +// responses. +type RedelegationResponse struct { + Redelegation + Entries []RedelegationEntryResponse `json:"entries"` // nolint: structtag +} + +// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +// contains a balance in addition to shares which is more suitable for client +// responses. +type RedelegationEntryResponse struct { + RedelegationEntry + Balance sdk.Int `json:"balance"` +} + +func NewRedelegationResponse(d sdk.AccAddress, vSrc, vDst sdk.ValAddress, entries []RedelegationEntryResponse) RedelegationResponse { + return RedelegationResponse{ + Redelegation{ + DelegatorAddress: d, + ValidatorSrcAddress: vSrc, + ValidatorDstAddress: vDst, + }, + entries, + } +} + +func NewRedelegationEntryResponse(ch int64, ct time.Time, s sdk.Dec, ib, b sdk.Int) RedelegationEntryResponse { + return RedelegationEntryResponse{NewRedelegationEntry(ch, ct, ib, s), b} +} + +// String implements the Stringer interface for RedelegationResp. +func (r RedelegationResponse) String() string { + out := fmt.Sprintf(`Redelegations between: + Delegator: %s + Source Validator: %s + Destination Validator: %s + Entries: +`, + r.DelegatorAddress, r.ValidatorSrcAddress, r.ValidatorDstAddress, + ) + + for i, entry := range r.Entries { + out += fmt.Sprintf(` Redelegation Entry #%d: + Creation height: %v + Min time to unbond (unix): %v + Initial Balance: %s + Shares: %s + Balance: %s +`, + i, entry.CreationHeight, entry.CompletionTime, entry.InitialBalance, entry.SharesDst, entry.Balance, + ) + } + + return strings.TrimRight(out, "\n") +} + +type redelegationRespAlias RedelegationResponse + +// MarshalJSON implements the json.Marshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (r RedelegationResponse) MarshalJSON() ([]byte, error) { + return json.Marshal((redelegationRespAlias)(r)) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (r *RedelegationResponse) UnmarshalJSON(bz []byte) error { + return json.Unmarshal(bz, (*redelegationRespAlias)(r)) +} + +// RedelegationResponses are a collection of RedelegationResp +type RedelegationResponses []RedelegationResponse + +func (r RedelegationResponses) String() (out string) { + for _, red := range r { + out += red.String() + "\n" + } + return strings.TrimSpace(out) +} diff --git a/x/staking/types/delegation_test.go b/x/staking/types/delegation_test.go index b3eec08fd3..c08090ca3f 100644 --- a/x/staking/types/delegation_test.go +++ b/x/staking/types/delegation_test.go @@ -1,11 +1,13 @@ package types import ( + "encoding/json" "testing" "time" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -75,3 +77,65 @@ func TestRedelegationString(t *testing.T) { require.NotEmpty(t, r.String()) } + +func TestDelegationResponses(t *testing.T) { + cdc := codec.New() + dr1 := NewDelegationResp(sdk.AccAddress(addr1), addr2, sdk.NewDec(5), sdk.NewInt(5)) + dr2 := NewDelegationResp(sdk.AccAddress(addr1), addr3, sdk.NewDec(5), sdk.NewInt(5)) + drs := DelegationResponses{dr1, dr2} + + bz1, err := json.Marshal(dr1) + require.NoError(t, err) + + bz2, err := cdc.MarshalJSON(dr1) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + bz1, err = json.Marshal(drs) + require.NoError(t, err) + + bz2, err = cdc.MarshalJSON(drs) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + var drs2 DelegationResponses + require.NoError(t, cdc.UnmarshalJSON(bz2, &drs2)) + require.Equal(t, drs, drs2) +} + +func TestRedelegationResponses(t *testing.T) { + cdc := codec.New() + entries := []RedelegationEntryResponse{ + NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + } + rdr1 := NewRedelegationResponse(sdk.AccAddress(addr1), addr2, addr3, entries) + rdr2 := NewRedelegationResponse(sdk.AccAddress(addr2), addr1, addr3, entries) + rdrs := RedelegationResponses{rdr1, rdr2} + + bz1, err := json.Marshal(rdr1) + require.NoError(t, err) + + bz2, err := cdc.MarshalJSON(rdr1) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + bz1, err = json.Marshal(rdrs) + require.NoError(t, err) + + bz2, err = cdc.MarshalJSON(rdrs) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + var rdrs2 RedelegationResponses + require.NoError(t, cdc.UnmarshalJSON(bz2, &rdrs2)) + + bz3, err := cdc.MarshalJSON(rdrs2) + require.NoError(t, err) + + require.Equal(t, bz2, bz3) +}