Skip to content

Commit

Permalink
x/Slashing: gRPC query service (#6597)
Browse files Browse the repository at this point in the history
* Add grpc methods - slashing

* Add slashing grpc queries

* Add tests

* Remove duplicate declarations

* Add signing infos

* Update query test

* Use suite for grpc tests

* Update godoc

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Jul 6, 2020
1 parent 3c76084 commit a0daec2
Show file tree
Hide file tree
Showing 7 changed files with 2,096 additions and 53 deletions.
52 changes: 52 additions & 0 deletions proto/cosmos/slashing/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
syntax = "proto3";
package cosmos.slashing;

import "cosmos/query/pagination.proto";
import "gogoproto/gogo.proto";
import "cosmos/slashing/slashing.proto";

option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types";

// Query provides defines the gRPC querier service
service Query {
// Params queries the parameters of slashing module
rpc Params (QueryParamsRequest) returns (QueryParamsResponse){}

// SigningInfo queries the signing info of given cons address
rpc SigningInfo (QuerySigningInfoRequest) returns (QuerySigningInfoResponse) {}

// SigningInfos queries signing info of all validators
rpc SigningInfos (QuerySigningInfosRequest) returns (QuerySigningInfosResponse) {}
}

// QueryParamsRequest is the request type for the Query/Parameters RPC method
message QueryParamsRequest{}

// QueryParamsResponse is the response type for the Query/Parameters RPC method
message QueryParamsResponse{
cosmos.slashing.Params params = 1[(gogoproto.nullable) = false];
}

// QuerySigningInfoRequest is the request type for the Query/SigningInfo RPC method
message QuerySigningInfoRequest{
// cons_address is the address to query signing info of
bytes cons_address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.ConsAddress"];
}

// QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC method
message QuerySigningInfoResponse{
// val_signing_info is the signing info of requested val cons address
cosmos.slashing.ValidatorSigningInfo val_signing_info = 1[(gogoproto.nullable)= false];
}

// QuerySigningInfosRequest is the request type for the Query/SigningInfos RPC method
message QuerySigningInfosRequest{
cosmos.query.PageRequest req = 1;
}

// QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC method
message QuerySigningInfosResponse{
// info is the signing info of all validators
repeated cosmos.slashing.ValidatorSigningInfo info = 1[(gogoproto.nullable)= false];
cosmos.query.PageResponse res =2;
}
23 changes: 23 additions & 0 deletions proto/cosmos/slashing/slashing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types";
option (gogoproto.equal_all) = true;

import "gogoproto/gogo.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

// MsgUnjail - struct for unjailing jailed validator
Expand Down Expand Up @@ -40,3 +41,25 @@ message ValidatorSigningInfo {
// missed blocks counter (to avoid scanning the array every time)
int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""];
}

// Params - used for initializing default parameter for slashing at genesis
message Params{
int64 signed_blocks_window = 1 [(gogoproto.jsontag) = "json:\"signed_blocks_window\"",
(gogoproto.moretags) = "yaml:\"signed_blocks_window\""];
bytes min_signed_per_window = 2 [(gogoproto.jsontag) = "json:\"min_signed_per_window\"",
(gogoproto.moretags) = "yaml:\"min_signed_per_window\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false];
google.protobuf.Duration downtime_jail_duration = 3 [(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.jsontag) = "json:\"downtime_jail_duration\"",
(gogoproto.moretags) = "yaml:\"downtime_jail_duration\""];
bytes slash_fraction_double_sign = 4 [(gogoproto.jsontag) = "json:\"slash_fraction_double_sign\"",
(gogoproto.moretags) = "yaml:\"slash_fraction_double_sign\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false];
bytes slash_fraction_downtime = 5 [(gogoproto.jsontag) = "json:\"slash_fraction_downtime\"",
(gogoproto.moretags) = "yaml:\"slash_fraction_downtime\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false];
}
69 changes: 69 additions & 0 deletions x/slashing/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package keeper

import (
"context"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
)

var _ types.QueryServer = Keeper{}

func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)
params := k.GetParams(ctx)

return &types.QueryParamsResponse{Params: params}, nil
}

func (k Keeper) SigningInfo(c context.Context, req *types.QuerySigningInfoRequest) (*types.QuerySigningInfoResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

if req.ConsAddress == nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid request")
}

ctx := sdk.UnwrapSDKContext(c)
signingInfo, found := k.GetValidatorSigningInfo(ctx, req.ConsAddress)
if !found {
return nil, status.Errorf(codes.NotFound, "SigningInfo not found for validator %s", req.ConsAddress)
}

return &types.QuerySigningInfoResponse{ValSigningInfo: signingInfo}, nil
}

func (k Keeper) SigningInfos(c context.Context, req *types.QuerySigningInfosRequest) (*types.QuerySigningInfosResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)
store := ctx.KVStore(k.storeKey)
var signInfos []types.ValidatorSigningInfo

sigInfoStore := prefix.NewStore(store, types.ValidatorSigningInfoKeyPrefix)
res, err := query.Paginate(sigInfoStore, req.Req, func(key []byte, value []byte) error {
var info types.ValidatorSigningInfo
err := k.cdc.UnmarshalBinaryBare(value, &info)
if err != nil {
return err
}
signInfos = append(signInfos, info)
return nil
})
if err != nil {
return nil, err
}
return &types.QuerySigningInfosResponse{Info: signInfos, Res: res}, nil
}
109 changes: 109 additions & 0 deletions x/slashing/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package keeper_test

import (
gocontext "context"
"testing"
"time"

"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/slashing/keeper"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
)

type SlashingTestSuite struct {
suite.Suite

app *simapp.SimApp
ctx sdk.Context
queryClient types.QueryClient
addrDels []sdk.AccAddress
}

func (suite *SlashingTestSuite) SetupTest() {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, abci.Header{})

app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams())
app.BankKeeper.SetSendEnabled(ctx, true)
app.SlashingKeeper.SetParams(ctx, keeper.TestParams())

addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.TokensFromConsensusPower(200))

info1 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[0]), int64(4), int64(3),
time.Unix(2, 0), false, int64(10))
info2 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[1]), int64(5), int64(4),
time.Unix(2, 0), false, int64(10))

app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1)
app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2)

suite.app = app
suite.ctx = ctx
suite.addrDels = addrDels

queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, app.SlashingKeeper)
queryClient := types.NewQueryClient(queryHelper)
suite.queryClient = queryClient
}

func (suite *SlashingTestSuite) TestGRPCQueryParams() {
queryClient := suite.queryClient
paramsResp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{})

suite.NoError(err)
suite.Equal(keeper.TestParams(), paramsResp.Params)
}

func (suite *SlashingTestSuite) TestGRPCSigningInfo() {
queryClient := suite.queryClient

infoResp, err := queryClient.SigningInfo(gocontext.Background(), &types.QuerySigningInfoRequest{ConsAddress: nil})
suite.Error(err)
suite.Nil(infoResp)

consAddr := sdk.ConsAddress(suite.addrDels[0])
info, found := suite.app.SlashingKeeper.GetValidatorSigningInfo(suite.ctx, consAddr)
suite.True(found)

infoResp, err = queryClient.SigningInfo(gocontext.Background(),
&types.QuerySigningInfoRequest{ConsAddress: consAddr})
suite.NoError(err)
suite.Equal(info, infoResp.ValSigningInfo)
}

func (suite *SlashingTestSuite) TestGRPCSigningInfos() {
queryClient := suite.queryClient

var signingInfos []types.ValidatorSigningInfo

suite.app.SlashingKeeper.IterateValidatorSigningInfos(suite.ctx, func(consAddr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) {
signingInfos = append(signingInfos, info)
return false
})

// verify all values are returned without pagination
var infoResp, err = queryClient.SigningInfos(gocontext.Background(),
&types.QuerySigningInfosRequest{Req: nil})
suite.NoError(err)
suite.Equal(signingInfos, infoResp.Info)

infoResp, err = queryClient.SigningInfos(gocontext.Background(),
&types.QuerySigningInfosRequest{Req: &query.PageRequest{Limit: 1, CountTotal: true}})
suite.NoError(err)
suite.Len(infoResp.Info, 1)
suite.Equal(signingInfos[0], infoResp.Info[0])
suite.NotNil(infoResp.Res.NextKey)
suite.Equal(uint64(2), infoResp.Res.Total)
}

func TestSlashingTestSuite(t *testing.T) {
suite.Run(t, new(SlashingTestSuite))
}
22 changes: 0 additions & 22 deletions x/slashing/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ func ParamKeyTable() paramtypes.KeyTable {
return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
}

// Params - used for initializing default parameter for slashing at genesis
type Params struct {
SignedBlocksWindow int64 `json:"signed_blocks_window" yaml:"signed_blocks_window"`
MinSignedPerWindow sdk.Dec `json:"min_signed_per_window" yaml:"min_signed_per_window"`
DowntimeJailDuration time.Duration `json:"downtime_jail_duration" yaml:"downtime_jail_duration"`
SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign" yaml:"slash_fraction_double_sign"`
SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime" yaml:"slash_fraction_downtime"`
}

// NewParams creates a new Params object
func NewParams(
signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration,
Expand All @@ -58,19 +49,6 @@ func NewParams(
}
}

// String implements the stringer interface for Params
func (p Params) String() string {
return fmt.Sprintf(`Slashing Params:
SignedBlocksWindow: %d
MinSignedPerWindow: %s
DowntimeJailDuration: %s
SlashFractionDoubleSign: %s
SlashFractionDowntime: %s`,
p.SignedBlocksWindow, p.MinSignedPerWindow,
p.DowntimeJailDuration, p.SlashFractionDoubleSign,
p.SlashFractionDowntime)
}

// ParamSetPairs - Implements params.ParamSet
func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
Expand Down
Loading

0 comments on commit a0daec2

Please sign in to comment.