Add route for querying signing_info for all validators (#3952)
Also remove duplicate pagination: - move function to extract query params into types/rest - adjust pagination values locally until available in tendermint for validators Code cleanup: - helper function in test - fix pagination description in swagger.yaml - uint instead of int when possible Closes: #3226 Closes: #3991
This commit is contained in:
committed by
Alessio Treglia
parent
e5897d8d91
commit
5bb6090e38
+102
-13
@@ -2,15 +2,14 @@ package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
||||
@@ -19,6 +18,11 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co
|
||||
signingInfoHandlerFn(cliCtx, slashing.StoreKey, cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/slashing/signing_infos",
|
||||
signingInfoHandlerListFn(cliCtx, slashing.StoreKey, cdc),
|
||||
).Methods("GET").Queries("page", "{page}", "limit", "{limit}")
|
||||
|
||||
r.HandleFunc(
|
||||
"/slashing/parameters",
|
||||
queryParamsHandlerFn(cdc, cliCtx),
|
||||
@@ -26,39 +30,79 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co
|
||||
}
|
||||
|
||||
// http request handler to query signing info
|
||||
// nolint: unparam
|
||||
func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *codec.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
pk, err := sdk.GetConsPubKeyBech32(vars["validatorPubKey"])
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(pk.Address()))
|
||||
signingInfo, code, err := getSigningInfo(cliCtx, storeName, cdc, pk.Address())
|
||||
|
||||
res, err := cliCtx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
rest.WriteErrorResponse(w, code, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
if code == http.StatusNoContent {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var signingInfo slashing.ValidatorSigningInfo
|
||||
rest.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent)
|
||||
}
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo)
|
||||
// http request handler to query signing info
|
||||
func signingInfoHandlerListFn(cliCtx context.CLIContext, storeName string, cdc *codec.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var signingInfoList []slashing.ValidatorSigningInfo
|
||||
|
||||
_, page, limit, err := rest.ParseHTTPArgs(r)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
height, err := rpc.GetChainHeight(cliCtx)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent)
|
||||
validators, err := rpc.GetValidators(cliCtx, &height)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(validators.Validators) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: this should happen when querying Validators from RPC,
|
||||
// as soon as it's available this is not needed anymore
|
||||
// parameter page is (page-1) because ParseHTTPArgs starts with page 1, where our array start with 0
|
||||
start, end := adjustPagination(uint(len(validators.Validators)), uint(page)-1, uint(limit))
|
||||
for _, validator := range validators.Validators[start:end] {
|
||||
address := validator.Address
|
||||
signingInfo, code, err := getSigningInfo(cliCtx, storeName, cdc, address)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, code, err.Error())
|
||||
return
|
||||
}
|
||||
signingInfoList = append(signingInfoList, signingInfo)
|
||||
}
|
||||
|
||||
if len(signingInfoList) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,3 +119,48 @@ func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Hand
|
||||
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
|
||||
}
|
||||
}
|
||||
|
||||
func getSigningInfo(cliCtx context.CLIContext, storeName string, cdc *codec.Codec, address []byte) (signingInfo slashing.ValidatorSigningInfo, code int, err error) {
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(address))
|
||||
|
||||
res, err := cliCtx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
code = http.StatusInternalServerError
|
||||
return
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
code = http.StatusNoContent
|
||||
return
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo)
|
||||
if err != nil {
|
||||
code = http.StatusInternalServerError
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust pagination with page starting from 0
|
||||
func adjustPagination(size, page, limit uint) (start uint, end uint) {
|
||||
// If someone asks for pages bigger than our dataset, just return everything
|
||||
if limit > size {
|
||||
return 0, size
|
||||
}
|
||||
|
||||
// Do pagination when healthy, fallback to 0
|
||||
start = 0
|
||||
if page*limit < size {
|
||||
start = page * limit
|
||||
}
|
||||
|
||||
// Do pagination only when healthy, fallback to len(dataset)
|
||||
end = size
|
||||
if start+limit <= size {
|
||||
end = start + limit
|
||||
}
|
||||
|
||||
return start, end
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAdjustPagination(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
size uint
|
||||
page uint
|
||||
limit uint
|
||||
start uint
|
||||
end uint
|
||||
}{
|
||||
{"Ok", 3, 0, 1, 0, 1},
|
||||
{"Limit too big", 3, 1, 5, 0, 3},
|
||||
{"Page over limit", 3, 2, 3, 0, 3},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
start, end := adjustPagination(tt.size, tt.page, tt.limit)
|
||||
require.Equal(t, tt.start, start)
|
||||
require.Equal(t, tt.end, end)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user