Skip to content

Commit

Permalink
supply querier (#4592)
Browse files Browse the repository at this point in the history
Add querier to the supply module.

Closes: #4082
  • Loading branch information
fedekunze authored and Alessio Treglia committed Jul 1, 2019
1 parent a485b9a commit 48e8161
Show file tree
Hide file tree
Showing 9 changed files with 464 additions and 4 deletions.
1 change: 1 addition & 0 deletions .pending/improvements/sdk/4595-supply-queriers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4082 supply module queriers for CLI and REST endpoints
2 changes: 1 addition & 1 deletion client/lcd/statik/statik.go

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ tags:
description: Slashing module APIs
- name: Distribution
description: Fee distribution module APIs
- name: Supply
description: Supply module APIs
- name: version
- name: Mint
description: Minting module APIs
- name: Misc
Expand Down Expand Up @@ -1827,6 +1830,43 @@ paths:
type: string
500:
description: Internal Server Error
/supply/total:
get:
summary: Total supply of coins in the chain
tags:
- Supply
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/Supply"
500:
description: Internal Server Error
/supply/total/{denomination}:
parameters:
- in: path
name: denomination
description: Coin denomination
required: true
type: string
x-example: uatom
get:
summary: Total supply of a single coin denomination
tags:
- Supply
produces:
- application/json
responses:
200:
description: OK
schema:
type: integer
400:
description: Invalid coin denomination
500:
description: Internal Server Error
definitions:
CheckTxResult:
type: object
Expand Down Expand Up @@ -2435,3 +2475,10 @@ definitions:
example: ""
value:
type: object
Supply:
type: object
properties:
total:
type: array
items:
$ref: "#/definitions/Coin"
104 changes: 104 additions & 0 deletions x/supply/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cli

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/supply/types"
"github.com/spf13/cobra"
)

// GetQueryCmd returns the cli query commands for this module
func GetQueryCmd(cdc *codec.Codec) *cobra.Command {
// Group supply queries under a subcommand
supplyQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the supply module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

supplyQueryCmd.AddCommand(client.GetCommands(
GetCmdQueryTotalSupply(cdc),
)...)

return supplyQueryCmd
}

// GetCmdQueryTotalSupply implements the query total supply command.
func GetCmdQueryTotalSupply(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "total [denom]",
Args: cobra.MaximumNArgs(1),
Short: "Query the total supply of coins of the chain",
Long: strings.TrimSpace(
fmt.Sprintf(`Query total supply of coins that are held by accounts in the
chain.
Example:
$ %s query %s total
To query for the total supply of a specific coin denomination use:
$ %s query %s total stake
`,
version.ClientName, types.ModuleName, version.ClientName, types.ModuleName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)

if len(args) == 0 {
return queryTotalSupply(cliCtx, cdc)
}
return querySupplyOf(cliCtx, cdc, args[0])
},
}
}

func queryTotalSupply(cliCtx context.CLIContext, cdc *codec.Codec) error {
params := types.NewQueryTotalSupplyParams(1, 0) // no pagination
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}

res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply), bz)
if err != nil {
return err
}

var totalSupply sdk.Coins
err = cdc.UnmarshalJSON(res, &totalSupply)
if err != nil {
return err
}

return cliCtx.PrintOutput(totalSupply)
}

func querySupplyOf(cliCtx context.CLIContext, cdc *codec.Codec, denom string) error {
params := types.NewQuerySupplyOfParams(denom)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}

res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySupplyOf), bz)
if err != nil {
return err
}

var supply sdk.Int
err = cdc.UnmarshalJSON(res, &supply)
if err != nil {
return err
}

return cliCtx.PrintOutput(supply)
}
90 changes: 90 additions & 0 deletions x/supply/client/rest/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package rest

import (
"fmt"
"net/http"

"github.com/gorilla/mux"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/supply/types"
)

// RegisterRoutes registers staking-related REST handlers to a router
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
registerQueryRoutes(cliCtx, r)
}

func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
// Query the total supply of coins
r.HandleFunc(
"/supply/total/",
totalSupplyHandlerFn(cliCtx),
).Methods("GET")

// Query the supply of a single denom
r.HandleFunc(
"/supply/total/{denom}",
supplyOfHandlerFn(cliCtx),
).Methods("GET")
}

// HTTP request handler to query the total supply of coins
func totalSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}

params := types.NewQueryTotalSupplyParams(page, limit)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}

// HTTP request handler to query the supply of a single denom
func supplyOfHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
denom := mux.Vars(r)["denom"]
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}

params := types.NewQuerySupplyOfParams(denom)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySupplyOf), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
78 changes: 78 additions & 0 deletions x/supply/keeper/querier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package keeper

import (
"fmt"

abci "github.com/tendermint/tendermint/abci/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/supply/types"
)

// NewQuerier creates a querier for supply REST endpoints
func NewQuerier(k Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
switch path[0] {
case types.QueryTotalSupply:
return queryTotalSupply(ctx, req, k)
case types.QuerySupplyOf:
return querySupplyOf(ctx, req, k)
default:
return nil, sdk.ErrUnknownRequest("unknown supply query endpoint")
}
}
}

func queryTotalSupply(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) {
var params types.QueryTotalSupplyParams

err := types.ModuleCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}

totalSupply := k.GetSupply(ctx).Total
totalSupplyLen := len(totalSupply)

if params.Limit == 0 {
params.Limit = totalSupplyLen
}

start := (params.Page - 1) * params.Limit
end := params.Limit + start
if end >= totalSupplyLen {
end = totalSupplyLen
}

if start >= totalSupplyLen {
// page is out of bounds
totalSupply = sdk.Coins{}
} else {
totalSupply = totalSupply[start:end]
}

res, err := totalSupply.MarshalJSON()
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
}

return res, nil
}

func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) {
var params types.QuerySupplyOfParams

err := types.ModuleCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}

supply := k.GetSupply(ctx).Total.AmountOf(params.Denom)

res, err := supply.MarshalJSON()
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
}

return res, nil
}
Loading

0 comments on commit 48e8161

Please sign in to comment.