x/evidence: gRPC query service (#6593)

* Add basic grpc query service for x/evidence

* Add grpc query test for AllEvidences query

* linting

* Add AnyUnpacker to query test helper and some var renaming

* Add test to check Evidence query result

* Update proto/cosmos/evidence/query.proto

Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>

* Use table tests

* Use NewQueryEvidenceRequest in place of QueryEvidenceParams

* Remove ConvertEvidence

Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Marie 2020-07-10 15:52:59 +02:00 committed by GitHub
parent 59cd0659a5
commit 1e23679066
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1386 additions and 25 deletions

View File

@ -0,0 +1,39 @@
syntax = "proto3";
package cosmos.evidence;
import "cosmos/query/pagination.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types";
// Query defines the gRPC querier service
service Query {
// Evidence queries evidence based on evidence hash
rpc Evidence(QueryEvidenceRequest) returns (QueryEvidenceResponse) {}
// AllEvidence queries all evidence
rpc AllEvidence(QueryAllEvidenceRequest) returns (QueryAllEvidenceResponse) {}
}
// QueryEvidenceRequest is the request type for the Query/Evidence RPC method
message QueryEvidenceRequest {
bytes evidence_hash = 1 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"];;
}
// QueryEvidenceResponse is the response type for the Query/Evidence RPC method
message QueryEvidenceResponse {
google.protobuf.Any evidence = 1;
}
// QueryEvidenceRequest is the request type for the Query/AllEvidence RPC method
message QueryAllEvidenceRequest {
cosmos.query.PageRequest req = 1;
}
// QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC method
message QueryAllEvidenceResponse {
repeated google.protobuf.Any evidence = 1;
cosmos.query.PageResponse res = 2;
}

View File

@ -5,6 +5,7 @@ import (
"sort"
ics23 "github.com/confio/ics23/go"
sdkmaps "github.com/cosmos/cosmos-sdk/store/rootmulti/internal/maps"
)

View File

@ -65,11 +65,12 @@ func QueryEvidenceCmd(cdc *codec.Codec) func(*cobra.Command, []string) error {
}
func queryEvidence(cdc *codec.Codec, clientCtx client.Context, hash string) error {
if _, err := hex.DecodeString(hash); err != nil {
decodedHash, err := hex.DecodeString(hash)
if err != nil {
return fmt.Errorf("invalid evidence hash: %w", err)
}
params := types.NewQueryEvidenceParams(hash)
params := types.NewQueryEvidenceRequest(decodedHash)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return fmt.Errorf("failed to marshal query params: %w", err)

View File

@ -1,6 +1,7 @@
package rest
import (
"encoding/hex"
"fmt"
"net/http"
"strings"
@ -39,7 +40,13 @@ func queryEvidenceHandler(clientCtx client.Context) http.HandlerFunc {
return
}
params := types.NewQueryEvidenceParams(evidenceHash)
decodedHash, err := hex.DecodeString(evidenceHash)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, "invalid evidence hash")
return
}
params := types.NewQueryEvidenceRequest(decodedHash)
bz, err := clientCtx.JSONMarshaler.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err))

View File

@ -0,0 +1,88 @@
package keeper
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/types/query"
proto "github.com/gogo/protobuf/proto"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/evidence/types"
)
var _ types.QueryServer = Keeper{}
// Evidence implements the Query/Evidence gRPC method
func (k Keeper) Evidence(c context.Context, req *types.QueryEvidenceRequest) (*types.QueryEvidenceResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
if req.EvidenceHash == nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid hash")
}
ctx := sdk.UnwrapSDKContext(c)
evidence, _ := k.GetEvidence(ctx, req.EvidenceHash)
if evidence == nil {
return nil, status.Errorf(codes.NotFound, "evidence %s not found", req.EvidenceHash)
}
msg, ok := evidence.(proto.Message)
if !ok {
return nil, status.Errorf(codes.Internal, "can't protomarshal %T", msg)
}
evidenceAny, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &types.QueryEvidenceResponse{Evidence: evidenceAny}, nil
}
// AllEvidence implements the Query/AllEvidence gRPC method
func (k Keeper) AllEvidence(c context.Context, req *types.QueryAllEvidenceRequest) (*types.QueryAllEvidenceResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c)
k.GetAllEvidence(ctx)
var evidence []*codectypes.Any
store := ctx.KVStore(k.storeKey)
evidenceStore := prefix.NewStore(store, types.KeyPrefixEvidence)
res, err := query.Paginate(evidenceStore, req.Req, func(key []byte, value []byte) error {
result, err := k.UnmarshalEvidence(value)
if err != nil {
return err
}
msg, ok := result.(proto.Message)
if !ok {
return status.Errorf(codes.Internal, "can't protomarshal %T", msg)
}
evidenceAny, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return err
}
evidence = append(evidence, evidenceAny)
return nil
})
if err != nil {
return &types.QueryAllEvidenceResponse{}, err
}
return &types.QueryAllEvidenceResponse{Evidence: evidence, Res: res}, nil
}

View File

@ -0,0 +1,143 @@
package keeper_test
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/evidence/exported"
"github.com/cosmos/cosmos-sdk/x/evidence/types"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
)
func (suite *KeeperTestSuite) TestQueryEvidence() {
var (
req *types.QueryEvidenceRequest
evidence []exported.Evidence
)
testCases := []struct {
msg string
malleate func()
expPass bool
posttests func(res *types.QueryEvidenceResponse)
}{
{
"empty request",
func() {
req = &types.QueryEvidenceRequest{}
},
false,
func(res *types.QueryEvidenceResponse) {},
},
{
"invalid request with empty evidence hash",
func() {
req = &types.QueryEvidenceRequest{EvidenceHash: tmbytes.HexBytes{}}
},
false,
func(res *types.QueryEvidenceResponse) {},
},
{
"success",
func() {
numEvidence := 100
evidence = suite.populateEvidence(suite.ctx, numEvidence)
req = types.NewQueryEvidenceRequest(evidence[0].Hash())
},
true,
func(res *types.QueryEvidenceResponse) {
var evi exported.Evidence
err := suite.app.InterfaceRegistry().UnpackAny(res.Evidence, &evi)
suite.Require().NoError(err)
suite.Require().NotNil(evi)
suite.Require().Equal(evi, evidence[0])
},
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest()
tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)
res, err := suite.queryClient.Evidence(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
} else {
suite.Require().Error(err)
suite.Require().Nil(res)
}
tc.posttests(res)
})
}
}
func (suite *KeeperTestSuite) TestQueryAllEvidence() {
var (
req *types.QueryAllEvidenceRequest
)
testCases := []struct {
msg string
malleate func()
expPass bool
posttests func(res *types.QueryAllEvidenceResponse)
}{
{
"success without evidence",
func() {
req = &types.QueryAllEvidenceRequest{}
},
true,
func(res *types.QueryAllEvidenceResponse) {
suite.Require().Empty(res.Evidence)
},
},
{
"success",
func() {
numEvidence := 100
_ = suite.populateEvidence(suite.ctx, numEvidence)
pageReq := &query.PageRequest{
Key: nil,
Limit: 50,
CountTotal: false,
}
req = types.NewQueryAllEvidenceRequest(pageReq)
},
true,
func(res *types.QueryAllEvidenceResponse) {
suite.Equal(len(res.Evidence), 50)
suite.NotNil(res.Res.NextKey)
},
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest()
tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)
res, err := suite.queryClient.AllEvidence(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
} else {
suite.Require().Error(err)
suite.Require().Nil(res)
}
tc.posttests(res)
})
}
}

View File

@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@ -73,6 +74,8 @@ type KeeperTestSuite struct {
ctx sdk.Context
querier sdk.Querier
app *simapp.SimApp
queryClient types.QueryClient
}
func (suite *KeeperTestSuite) SetupTest() {
@ -97,6 +100,10 @@ func (suite *KeeperTestSuite) SetupTest() {
addr := sdk.AccAddress(addr)
app.AccountKeeper.SetAccount(suite.ctx, authtypes.NewBaseAccount(addr, pubkeys[i], uint64(i), 0))
}
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry())
types.RegisterQueryServer(queryHelper, app.EvidenceKeeper)
suite.queryClient = types.NewQueryClient(queryHelper)
}
func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []exported.Evidence {

View File

@ -1,8 +1,6 @@
package keeper
import (
"encoding/hex"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -36,21 +34,16 @@ func NewQuerier(k Keeper) sdk.Querier {
}
func queryEvidence(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
var params types.QueryEvidenceParams
var params types.QueryEvidenceRequest
err := k.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
hash, err := hex.DecodeString(params.EvidenceHash)
if err != nil {
return nil, sdkerrors.Wrap(err, "failed to decode evidence hash string query")
}
evidence, ok := k.GetEvidence(ctx, hash)
evidence, ok := k.GetEvidence(ctx, params.EvidenceHash)
if !ok {
return nil, sdkerrors.Wrap(types.ErrNoEvidenceExists, params.EvidenceHash)
return nil, sdkerrors.Wrap(types.ErrNoEvidenceExists, params.EvidenceHash.String())
}
res, err := codec.MarshalJSONIndent(k.cdc, evidence)

View File

@ -15,7 +15,7 @@ const (
custom = "custom"
)
func (suite *KeeperTestSuite) TestQueryEvidence_Existing() {
func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_Existing() {
ctx := suite.ctx.WithIsCheckTx(false)
numEvidence := 100
cdc, _ := simapp.MakeCodecs()
@ -23,7 +23,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_Existing() {
evidence := suite.populateEvidence(ctx, numEvidence)
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams(evidence[0].Hash().String())),
Data: cdc.MustMarshalJSON(types.NewQueryEvidenceRequest(evidence[0].Hash())),
}
bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query)
@ -35,7 +35,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_Existing() {
suite.Equal(evidence[0], e)
}
func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() {
func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_NonExisting() {
ctx := suite.ctx.WithIsCheckTx(false)
cdc, _ := simapp.MakeCodecs()
numEvidence := 100
@ -43,7 +43,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() {
suite.populateEvidence(ctx, numEvidence)
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams("0000000000000000000000000000000000000000000000000000000000000000")),
Data: cdc.MustMarshalJSON(types.NewQueryEvidenceRequest([]byte("0000000000000000000000000000000000000000000000000000000000000000"))),
}
bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query)
@ -51,7 +51,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() {
suite.Nil(bz)
}
func (suite *KeeperTestSuite) TestQueryAllEvidence() {
func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence() {
ctx := suite.ctx.WithIsCheckTx(false)
cdc, _ := simapp.MakeCodecs()
numEvidence := 100
@ -71,7 +71,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence() {
suite.Len(e, numEvidence)
}
func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() {
func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence_InvalidPagination() {
ctx := suite.ctx.WithIsCheckTx(false)
cdc, _ := simapp.MakeCodecs()
numEvidence := 100

View File

@ -1,18 +1,25 @@
package types
import (
tmbytes "github.com/tendermint/tendermint/libs/bytes"
query "github.com/cosmos/cosmos-sdk/types/query"
)
// Querier routes for the evidence module
const (
QueryEvidence = "evidence"
QueryAllEvidence = "all_evidence"
)
// QueryEvidenceParams defines the parameters necessary for querying Evidence.
type QueryEvidenceParams struct {
EvidenceHash string `json:"evidence_hash" yaml:"evidence_hash"`
// NewQueryEvidenceRequest creates a new instance of QueryEvidenceRequest.
func NewQueryEvidenceRequest(hash tmbytes.HexBytes) *QueryEvidenceRequest {
return &QueryEvidenceRequest{EvidenceHash: hash}
}
func NewQueryEvidenceParams(hash string) QueryEvidenceParams {
return QueryEvidenceParams{EvidenceHash: hash}
// NewQueryAllEvidenceRequest creates a new instance of QueryAllEvidenceRequest.
func NewQueryAllEvidenceRequest(req *query.PageRequest) *QueryAllEvidenceRequest {
return &QueryAllEvidenceRequest{Req: req}
}
// QueryAllEvidenceParams defines the parameters necessary for querying for all Evidence.

1075
x/evidence/types/query.pb.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,8 @@ import (
context "context"
fmt "fmt"
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
grpc1 "github.com/gogo/protobuf/grpc"
_ "github.com/gogo/protobuf/gogoproto"
grpc1 "github.com/gogo/protobuf/grpc"
proto "github.com/gogo/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"