2018-04-23 08:30:54 -07:00
|
|
|
package rest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/context"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/wire"
|
2018-07-03 20:44:54 -07:00
|
|
|
|
2018-04-23 08:30:54 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
2018-07-03 20:44:54 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
2018-04-23 08:30:54 -07:00
|
|
|
)
|
|
|
|
|
2018-06-29 15:22:24 -07:00
|
|
|
const storeName = "stake"
|
|
|
|
|
2018-05-19 22:43:53 -07:00
|
|
|
func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
r.HandleFunc(
|
|
|
|
"/stake/{delegator}/delegation/{validator}",
|
2018-06-29 15:22:24 -07:00
|
|
|
delegationHandlerFn(ctx, cdc),
|
2018-06-26 19:00:12 -07:00
|
|
|
).Methods("GET")
|
|
|
|
|
|
|
|
r.HandleFunc(
|
|
|
|
"/stake/{delegator}/ubd/{validator}",
|
2018-06-29 15:22:24 -07:00
|
|
|
ubdHandlerFn(ctx, cdc),
|
2018-06-26 19:00:12 -07:00
|
|
|
).Methods("GET")
|
|
|
|
|
2018-05-24 08:14:10 -07:00
|
|
|
r.HandleFunc(
|
2018-06-26 19:00:12 -07:00
|
|
|
"/stake/{delegator}/red/{validator_src}/{validator_dst}",
|
2018-06-29 15:22:24 -07:00
|
|
|
redHandlerFn(ctx, cdc),
|
2018-05-24 08:14:10 -07:00
|
|
|
).Methods("GET")
|
2018-06-26 19:00:12 -07:00
|
|
|
|
2018-05-28 23:07:03 -07:00
|
|
|
r.HandleFunc(
|
|
|
|
"/stake/validators",
|
2018-06-29 15:22:24 -07:00
|
|
|
validatorsHandlerFn(ctx, cdc),
|
2018-05-28 23:07:03 -07:00
|
|
|
).Methods("GET")
|
2018-04-23 08:30:54 -07:00
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// http request handler to query a delegation
|
2018-06-29 15:22:24 -07:00
|
|
|
func delegationHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
2018-04-23 08:30:54 -07:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2018-05-30 07:42:59 -07:00
|
|
|
|
2018-04-23 08:30:54 -07:00
|
|
|
// read parameters
|
|
|
|
vars := mux.Vars(r)
|
2018-06-05 21:53:04 -07:00
|
|
|
bech32delegator := vars["delegator"]
|
|
|
|
bech32validator := vars["validator"]
|
2018-04-23 08:30:54 -07:00
|
|
|
|
2018-06-05 21:53:04 -07:00
|
|
|
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
2018-04-23 08:30:54 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-05 21:53:04 -07:00
|
|
|
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
|
2018-04-23 08:30:54 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-02 15:16:47 -07:00
|
|
|
key := stake.GetDelegationKey(delegatorAddr, validatorAddr)
|
2018-04-23 08:30:54 -07:00
|
|
|
|
2018-06-13 22:49:21 -07:00
|
|
|
res, err := ctx.QueryStore(key, storeName)
|
2018-04-23 08:30:54 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2018-06-26 19:00:12 -07:00
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error())))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// the query will return empty if there is no data for this record
|
|
|
|
if len(res) == 0 {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-03 20:44:54 -07:00
|
|
|
delegation := types.UnmarshalDelegation(cdc, key, res)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
output, err := cdc.MarshalJSON(delegation)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Write(output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// http request handler to query an unbonding-delegation
|
2018-06-29 15:22:24 -07:00
|
|
|
func ubdHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
2018-06-26 19:00:12 -07:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
// read parameters
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bech32delegator := vars["delegator"]
|
|
|
|
bech32validator := vars["validator"]
|
|
|
|
|
|
|
|
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-02 15:16:47 -07:00
|
|
|
key := stake.GetUBDKey(delegatorAddr, validatorAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
res, err := ctx.QueryStore(key, storeName)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error())))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// the query will return empty if there is no data for this record
|
|
|
|
if len(res) == 0 {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var ubd stake.UnbondingDelegation
|
|
|
|
err = cdc.UnmarshalBinary(res, &ubd)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't decode unbonding-delegation. Error: %s", err.Error())))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
output, err := cdc.MarshalJSON(ubd)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Write(output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// http request handler to query an redelegation
|
2018-06-29 15:22:24 -07:00
|
|
|
func redHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
2018-06-26 19:00:12 -07:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
// read parameters
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bech32delegator := vars["delegator"]
|
|
|
|
bech32validatorSrc := vars["validator_src"]
|
|
|
|
bech32validatorDst := vars["validator_dst"]
|
|
|
|
|
|
|
|
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
validatorSrcAddr, err := sdk.GetValAddressBech32(bech32validatorSrc)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
validatorDstAddr, err := sdk.GetValAddressBech32(bech32validatorDst)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-02 15:16:47 -07:00
|
|
|
key := stake.GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
2018-06-26 19:00:12 -07:00
|
|
|
|
|
|
|
res, err := ctx.QueryStore(key, storeName)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't query redelegation. Error: %s", err.Error())))
|
2018-04-23 08:30:54 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
// the query will return empty if there is no data for this record
|
2018-04-23 08:30:54 -07:00
|
|
|
if len(res) == 0 {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
var red stake.Redelegation
|
|
|
|
err = cdc.UnmarshalBinary(res, &red)
|
2018-04-23 08:30:54 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2018-06-26 19:00:12 -07:00
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't decode redelegation. Error: %s", err.Error())))
|
2018-04-23 08:30:54 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-26 19:00:12 -07:00
|
|
|
output, err := cdc.MarshalJSON(red)
|
2018-04-23 08:30:54 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Write(output)
|
|
|
|
}
|
|
|
|
}
|
2018-05-28 23:07:03 -07:00
|
|
|
|
2018-06-11 18:12:37 -07:00
|
|
|
// TODO move exist next to validator struct for maintainability
|
2018-06-05 21:53:04 -07:00
|
|
|
type StakeValidatorOutput struct {
|
|
|
|
Owner string `json:"owner"` // in bech32
|
|
|
|
PubKey string `json:"pub_key"` // in bech32
|
|
|
|
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
|
|
|
|
|
|
|
PoolShares stake.PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
|
|
|
|
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
|
|
|
|
|
|
|
|
Description stake.Description `json:"description"` // description terms for the validator
|
|
|
|
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
|
|
|
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
|
|
|
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
|
|
|
|
|
|
|
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
|
|
|
CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
|
|
|
CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
|
|
|
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
|
|
|
|
|
|
|
// fee related
|
|
|
|
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
|
|
|
}
|
|
|
|
|
|
|
|
func bech32StakeValidatorOutput(validator stake.Validator) (StakeValidatorOutput, error) {
|
|
|
|
bechOwner, err := sdk.Bech32ifyVal(validator.Owner)
|
|
|
|
if err != nil {
|
|
|
|
return StakeValidatorOutput{}, err
|
|
|
|
}
|
|
|
|
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
|
|
|
|
if err != nil {
|
|
|
|
return StakeValidatorOutput{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return StakeValidatorOutput{
|
|
|
|
Owner: bechOwner,
|
|
|
|
PubKey: bechValPubkey,
|
|
|
|
Revoked: validator.Revoked,
|
|
|
|
|
|
|
|
PoolShares: validator.PoolShares,
|
|
|
|
DelegatorShares: validator.DelegatorShares,
|
|
|
|
|
|
|
|
Description: validator.Description,
|
|
|
|
BondHeight: validator.BondHeight,
|
|
|
|
BondIntraTxCounter: validator.BondIntraTxCounter,
|
|
|
|
ProposerRewardPool: validator.ProposerRewardPool,
|
|
|
|
|
|
|
|
Commission: validator.Commission,
|
|
|
|
CommissionMax: validator.CommissionMax,
|
|
|
|
CommissionChangeRate: validator.CommissionChangeRate,
|
|
|
|
CommissionChangeToday: validator.CommissionChangeToday,
|
|
|
|
|
|
|
|
PrevBondedShares: validator.PrevBondedShares,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO bech32
|
2018-05-28 23:07:03 -07:00
|
|
|
// http request handler to query list of validators
|
2018-06-29 15:22:24 -07:00
|
|
|
func validatorsHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
2018-05-28 23:07:03 -07:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2018-05-30 08:16:09 -07:00
|
|
|
kvs, err := ctx.QuerySubspace(cdc, stake.ValidatorsKey, storeName)
|
2018-05-28 23:07:03 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2018-06-13 12:13:22 -07:00
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error())))
|
2018-05-28 23:07:03 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// the query will return empty if there are no validators
|
2018-05-30 08:16:09 -07:00
|
|
|
if len(kvs) == 0 {
|
2018-05-28 23:07:03 -07:00
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-30 07:42:59 -07:00
|
|
|
// parse out the validators
|
2018-06-05 21:53:04 -07:00
|
|
|
validators := make([]StakeValidatorOutput, len(kvs))
|
2018-05-30 09:00:00 -07:00
|
|
|
for i, kv := range kvs {
|
2018-05-28 23:07:03 -07:00
|
|
|
var validator stake.Validator
|
2018-06-05 21:53:04 -07:00
|
|
|
var bech32Validator StakeValidatorOutput
|
2018-05-28 23:07:03 -07:00
|
|
|
err = cdc.UnmarshalBinary(kv.Value, &validator)
|
2018-06-05 21:53:04 -07:00
|
|
|
if err == nil {
|
|
|
|
bech32Validator, err = bech32StakeValidatorOutput(validator)
|
|
|
|
}
|
2018-05-28 23:07:03 -07:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2018-06-13 12:13:22 -07:00
|
|
|
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
|
2018-05-28 23:07:03 -07:00
|
|
|
return
|
|
|
|
}
|
2018-06-05 21:53:04 -07:00
|
|
|
validators[i] = bech32Validator
|
2018-05-28 23:07:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
output, err := cdc.MarshalJSON(validators)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Write(output)
|
|
|
|
}
|
|
|
|
}
|