cosmos-sdk/x/staking/client/rest/query.go
Amaury 1b00c01b70
Audit through legacy endpoints to find breaking changes (#8037)
* Add error msg on staking

* Add tests for legacy staking and gov

* Add test for encode

* Fix broadcast too

* Add comments

* update changelog

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2020-12-03 23:33:32 +00:00

397 lines
12 KiB
Go

package rest
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client"
clientrest "github.com/cosmos/cosmos-sdk/client/rest"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func registerQueryRoutes(clientCtx client.Context, r *mux.Router) {
// Get all delegations from a delegator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/delegations",
delegatorDelegationsHandlerFn(clientCtx),
).Methods("GET")
// Get all unbonding delegations from a delegator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/unbonding_delegations",
delegatorUnbondingDelegationsHandlerFn(clientCtx),
).Methods("GET")
// Get all staking txs (i.e msgs) from a delegator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/txs",
delegatorTxsHandlerFn(clientCtx),
).Methods("GET")
// Query all validators that a delegator is bonded to
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/validators",
delegatorValidatorsHandlerFn(clientCtx),
).Methods("GET")
// Query a validator that a delegator is bonded to
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/validators/{validatorAddr}",
delegatorValidatorHandlerFn(clientCtx),
).Methods("GET")
// Query a delegation between a delegator and a validator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/delegations/{validatorAddr}",
delegationHandlerFn(clientCtx),
).Methods("GET")
// Query all unbonding delegations between a delegator and a validator
r.HandleFunc(
"/staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}",
unbondingDelegationHandlerFn(clientCtx),
).Methods("GET")
// Query redelegations (filters in query params)
r.HandleFunc(
"/staking/redelegations",
redelegationsHandlerFn(clientCtx),
).Methods("GET")
// Get all validators
r.HandleFunc(
"/staking/validators",
validatorsHandlerFn(clientCtx),
).Methods("GET")
// Get a single validator info
r.HandleFunc(
"/staking/validators/{validatorAddr}",
validatorHandlerFn(clientCtx),
).Methods("GET")
// Get all delegations to a validator
r.HandleFunc(
"/staking/validators/{validatorAddr}/delegations",
validatorDelegationsHandlerFn(clientCtx),
).Methods("GET")
// Get all unbonding delegations from a validator
r.HandleFunc(
"/staking/validators/{validatorAddr}/unbonding_delegations",
validatorUnbondingDelegationsHandlerFn(clientCtx),
).Methods("GET")
// Get HistoricalInfo at a given height
r.HandleFunc(
"/staking/historical_info/{height}",
historicalInfoHandlerFn(clientCtx),
).Methods("GET")
// Get the current state of the staking pool
r.HandleFunc(
"/staking/pool",
poolHandlerFn(clientCtx),
).Methods("GET")
// Get the current staking parameter values
r.HandleFunc(
"/staking/parameters",
paramsHandlerFn(clientCtx),
).Methods("GET")
}
// HTTP request handler to query a delegator delegations
func delegatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return queryDelegator(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorDelegations))
}
// HTTP request handler to query a delegator unbonding delegations
func delegatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorUnbondingDelegations))
}
// HTTP request handler to query all staking txs (msgs) from a delegator
func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var typesQuerySlice []string
vars := mux.Vars(r)
delegatorAddr := vars["delegatorAddr"]
if _, err := sdk.AccAddressFromBech32(delegatorAddr); rest.CheckBadRequestError(w, err) {
return
}
clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}
typesQuery := r.URL.Query().Get("type")
trimmedQuery := strings.TrimSpace(typesQuery)
if len(trimmedQuery) != 0 {
typesQuerySlice = strings.Split(trimmedQuery, " ")
}
noQuery := len(typesQuerySlice) == 0
isBondTx := contains(typesQuerySlice, "bond")
isUnbondTx := contains(typesQuerySlice, "unbond")
isRedTx := contains(typesQuerySlice, "redelegate")
var (
txs []*sdk.SearchTxsResult
actions []string
)
switch {
case isBondTx:
actions = append(actions, types.MsgDelegate{}.Type())
case isUnbondTx:
actions = append(actions, types.MsgUndelegate{}.Type())
case isRedTx:
actions = append(actions, types.MsgBeginRedelegate{}.Type())
case noQuery:
actions = append(actions, types.MsgDelegate{}.Type())
actions = append(actions, types.MsgUndelegate{}.Type())
actions = append(actions, types.MsgBeginRedelegate{}.Type())
default:
w.WriteHeader(http.StatusNoContent)
return
}
for _, action := range actions {
foundTxs, errQuery := queryTxs(clientCtx, action, delegatorAddr)
if rest.CheckInternalServerError(w, errQuery) {
return
}
txs = append(txs, foundTxs)
}
res, err := clientCtx.LegacyAmino.MarshalJSON(txs)
if rest.CheckInternalServerError(w, err) {
return
}
rest.PostProcessResponseBare(w, clientCtx, res)
}
}
// HTTP request handler to query an unbonding-delegation
func unbondingDelegationHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryUnbondingDelegation))
}
// HTTP request handler to query redelegations
func redelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var params types.QueryRedelegationParams
clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}
bechDelegatorAddr := r.URL.Query().Get("delegator")
bechSrcValidatorAddr := r.URL.Query().Get("validator_from")
bechDstValidatorAddr := r.URL.Query().Get("validator_to")
if len(bechDelegatorAddr) != 0 {
delegatorAddr, err := sdk.AccAddressFromBech32(bechDelegatorAddr)
if rest.CheckBadRequestError(w, err) {
return
}
params.DelegatorAddr = delegatorAddr
}
if len(bechSrcValidatorAddr) != 0 {
srcValidatorAddr, err := sdk.ValAddressFromBech32(bechSrcValidatorAddr)
if rest.CheckBadRequestError(w, err) {
return
}
params.SrcValidatorAddr = srcValidatorAddr
}
if len(bechDstValidatorAddr) != 0 {
dstValidatorAddr, err := sdk.ValAddressFromBech32(bechDstValidatorAddr)
if rest.CheckBadRequestError(w, err) {
return
}
params.DstValidatorAddr = dstValidatorAddr
}
bz, err := clientCtx.LegacyAmino.MarshalJSON(params)
if rest.CheckBadRequestError(w, err) {
return
}
res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryRedelegations), bz)
if rest.CheckInternalServerError(w, err) {
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}
// HTTP request handler to query a delegation
func delegationHandlerFn(clientCtx client.Context) http.HandlerFunc {
return queryBonds(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegation))
}
// HTTP request handler to query all delegator bonded validators
func delegatorValidatorsHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidators))
}
// HTTP request handler to get information from a currently bonded validator
func delegatorValidatorHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidator))
}
// HTTP request handler to query list of validators
func validatorsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if rest.CheckBadRequestError(w, err) {
return
}
clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}
status := r.FormValue("status")
// These are query params that were available in =<0.39. We show a nice
// error message for this breaking change.
if status == "bonded" || status == "unbonding" || status == "unbonded" {
err := fmt.Errorf("cosmos sdk v0.40 introduces a breaking change on this endpoint:"+
" instead of querying using `?status=%s`, please use `status=BOND_STATUS_%s`. For more"+
" info, please see our REST endpoint migration guide at %s", status, strings.ToUpper(status), clientrest.DeprecationURL)
if rest.CheckBadRequestError(w, err) {
return
}
}
if status == "" {
status = types.BondStatusBonded
}
params := types.NewQueryValidatorsParams(page, limit, status)
bz, err := clientCtx.LegacyAmino.MarshalJSON(params)
if rest.CheckBadRequestError(w, err) {
return
}
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidators)
res, height, err := clientCtx.QueryWithData(route, bz)
if rest.CheckInternalServerError(w, err) {
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}
// HTTP request handler to query the validator information from a given validator address
func validatorHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidator))
}
// HTTP request handler to query all unbonding delegations from a validator
func validatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return queryValidator(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorDelegations))
}
// HTTP request handler to query all unbonding delegations from a validator
func validatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc {
return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorUnbondingDelegations))
}
// HTTP request handler to query historical info at a given height
func historicalInfoHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
heightStr := vars["height"]
height, err := strconv.ParseInt(heightStr, 10, 64)
if err != nil || height < 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Must provide non-negative integer for height: %v", err))
return
}
params := types.QueryHistoricalInfoRequest{Height: height}
bz, err := clientCtx.LegacyAmino.MarshalJSON(params)
if rest.CheckInternalServerError(w, err) {
return
}
res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalInfo), bz)
if rest.CheckBadRequestError(w, err) {
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}
// HTTP request handler to query the pool information
func poolHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}
res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPool), nil)
if rest.CheckInternalServerError(w, err) {
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}
// HTTP request handler to query the staking params values
func paramsHandlerFn(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r)
if !ok {
return
}
res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil)
if rest.CheckInternalServerError(w, err) {
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}