Skip to content

Commit

Permalink
x/auth: gRPC query service (cosmos#6565)
Browse files Browse the repository at this point in the history
Co-authored-by: sahith-narahari <[email protected]>
Co-authored-by: Anil Kumar Kammari <[email protected]>
  • Loading branch information
3 people authored Jul 8, 2020
1 parent e6bb2e7 commit 4536ca2
Show file tree
Hide file tree
Showing 10 changed files with 1,216 additions and 11 deletions.
36 changes: 36 additions & 0 deletions proto/cosmos/auth/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
syntax = "proto3";
package cosmos.auth;

import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
import "cosmos/auth/auth.proto";
import "cosmos_proto/cosmos.proto";

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

// Query creates service with Account and Parameters as rpc
service Query{
// Account returns account details based on address
rpc Account (QueryAccountRequest) returns (QueryAccountResponse) {}

// Parameters queries all params
rpc Parameters (QueryParametersRequest) returns (QueryParametersResponse) {}
}

// QueryAccountRequest is request type for the Query/Account RPC method
message QueryAccountRequest{
bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
}

// QueryAccountResponse is response type for the Query/Account RPC method
message QueryAccountResponse{
google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"];
}

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

// QueryParametersResponse is response type for the Query/Parameters RPC method
message QueryParametersResponse{
cosmos.auth.Params params = 1 [(gogoproto.nullable) = false];
}
67 changes: 67 additions & 0 deletions x/auth/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package keeper

import (
"context"
"fmt"

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

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/auth/types"
)

var _ types.QueryServer = AccountKeeper{}

// Account returns account details based on address
func (k AccountKeeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

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

ctx := sdk.UnwrapSDKContext(c)
account := k.GetAccount(ctx, req.Address)
if account == nil {
return nil, status.Errorf(codes.NotFound, "account %s not found", req.Address)
}

acc, err := ConvertAccount(account)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}

return &types.QueryAccountResponse{Account: acc}, nil
}

// Parameters returns parameters of auth module
func (k AccountKeeper) Parameters(c context.Context, req *types.QueryParametersRequest) (*types.QueryParametersResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c)
params := k.GetParams(ctx)

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

// ConvertAccount converts AccountI to Any type
func ConvertAccount(account types.AccountI) (*codectypes.Any, error) {
msg, ok := account.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't protomarshal %T", msg)
}

any, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, err
}

return any, nil
}
134 changes: 134 additions & 0 deletions x/auth/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package keeper_test

import (
"fmt"

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

func (suite *KeeperTestSuite) TestGRPCQueryAccount() {
var (
req *types.QueryAccountRequest
)
_, _, addr := types.KeyTestPubAddr()

testCases := []struct {
msg string
malleate func()
expPass bool
posttests func(res *types.QueryAccountResponse)
}{
{
"empty request",
func() {
req = &types.QueryAccountRequest{}
},
false,
func(res *types.QueryAccountResponse) {},
},
{
"invalid request",
func() {
req = &types.QueryAccountRequest{Address: []byte("")}
},
false,
func(res *types.QueryAccountResponse) {},
},
{
"invalid request with empty byte array",
func() {
req = &types.QueryAccountRequest{Address: []byte{}}
},
false,
func(res *types.QueryAccountResponse) {},
},
{
"account not found",
func() {
req = &types.QueryAccountRequest{Address: addr}
},
false,
func(res *types.QueryAccountResponse) {},
},
{
"success",
func() {
suite.app.AccountKeeper.SetAccount(suite.ctx,
suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr))
req = &types.QueryAccountRequest{Address: addr}
},
true,
func(res *types.QueryAccountResponse) {
var newAccount types.AccountI
err := suite.app.InterfaceRegistry().UnpackAny(res.Account, &newAccount)
suite.Require().NoError(err)
suite.Require().NotNil(newAccount)
suite.Require().True(addr.Equals(newAccount.GetAddress()))
},
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)

res, err := suite.queryClient.Account(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) TestGRPCQueryParameters() {
var (
req *types.QueryParametersRequest
expParams types.Params
)

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"success",
func() {
req = &types.QueryParametersRequest{}
expParams = suite.app.AccountKeeper.GetParams(suite.ctx)
},
true,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)

res, err := suite.queryClient.Parameters(ctx, req)

if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expParams, res.Params)
} else {
suite.Require().Error(err)
suite.Require().Nil(res)
}
})
}
}
23 changes: 23 additions & 0 deletions x/auth/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"testing"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/keeper"
Expand All @@ -22,6 +24,27 @@ var (
randomPermAcc = types.NewEmptyModuleAccount(randomPerm, "random")
)

type KeeperTestSuite struct {
suite.Suite

app *simapp.SimApp
ctx sdk.Context

queryClient types.QueryClient
}

func (suite *KeeperTestSuite) SetupTest() {
suite.app, suite.ctx = createTestApp(true)

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

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}

func TestAccountMapperGetSet(t *testing.T) {
app, ctx := createTestApp(true)
addr := sdk.AccAddress([]byte("some-address"))
Expand Down
2 changes: 1 addition & 1 deletion x/auth/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewQuerier(k AccountKeeper) sdk.Querier {
}

func queryAccount(ctx sdk.Context, req abci.RequestQuery, k AccountKeeper) ([]byte, error) {
var params types.QueryAccountParams
var params types.QueryAccountRequest
if err := k.cdc.UnmarshalJSON(req.Data, &params); err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions x/auth/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ func TestQueryAccount(t *testing.T) {
require.Error(t, err)
require.Nil(t, res)

req.Data = cdc.MustMarshalJSON(types.NewQueryAccountParams([]byte("")))
req.Data = cdc.MustMarshalJSON(types.NewQueryAccountRequest([]byte("")))
res, err = querier(ctx, path, req)
require.Error(t, err)
require.Nil(t, res)

_, _, addr := types.KeyTestPubAddr()
req.Data = cdc.MustMarshalJSON(types.NewQueryAccountParams(addr))
req.Data = cdc.MustMarshalJSON(types.NewQueryAccountRequest(addr))
res, err = querier(ctx, path, req)
require.Error(t, err)
require.Nil(t, res)
Expand Down
2 changes: 1 addition & 1 deletion x/auth/types/account_retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (ar AccountRetriever) GetAccount(querier client.NodeQuerier, addr sdk.AccAd
// height of the query with the account. An error is returned if the query
// or decoding fails.
func (ar AccountRetriever) GetAccountWithHeight(querier client.NodeQuerier, addr sdk.AccAddress) (AccountI, int64, error) {
bs, err := ar.codec.MarshalJSON(NewQueryAccountParams(addr))
bs, err := ar.codec.MarshalJSON(NewQueryAccountRequest(addr))
if err != nil {
return nil, 0, err
}
Expand Down
2 changes: 1 addition & 1 deletion x/auth/types/account_retriever_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestAccountRetriever(t *testing.T) {
mockNodeQuerier := mocks.NewMockNodeQuerier(mockCtrl)
accRetr := types.NewAccountRetriever(appCodec)
addr := []byte("test")
bs, err := appCodec.MarshalJSON(types.NewQueryAccountParams(addr))
bs, err := appCodec.MarshalJSON(types.NewQueryAccountRequest(addr))
require.NoError(t, err)

route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAccount)
Expand Down
12 changes: 6 additions & 6 deletions x/auth/types/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ const (
QueryParams = "params"
)

// QueryAccountParams defines the params for querying accounts.
type QueryAccountParams struct {
Address sdk.AccAddress `json:"account"`
// NewQueryAccountRequest creates a new instance of QueryAccountRequest.
func NewQueryAccountRequest(addr sdk.AccAddress) *QueryAccountRequest {
return &QueryAccountRequest{Address: addr}
}

// NewQueryAccountParams creates a new instance of QueryAccountParams.
func NewQueryAccountParams(addr sdk.AccAddress) QueryAccountParams {
return QueryAccountParams{Address: addr}
// NewQueryParametersRequest creates a new instance of QueryParametersRequest.
func NewQueryParametersRequest() *QueryParametersRequest {
return &QueryParametersRequest{}
}
Loading

0 comments on commit 4536ca2

Please sign in to comment.