Skip to content

Commit

Permalink
feat(cli): add module-account cli cmd and grpc get api (#13612)
Browse files Browse the repository at this point in the history
  • Loading branch information
gsk967 authored Oct 22, 2022
1 parent e279271 commit ddf5cf0
Show file tree
Hide file tree
Showing 10 changed files with 2,717 additions and 1,013 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/bank) [#11981](https://github.com/cosmos/cosmos-sdk/pull/11981) Create the `SetSendEnabled` endpoint for managing the bank's SendEnabled settings.
* (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info.
* (x/consensus) [#12905](https://github.com/cosmos/cosmos-sdk/pull/12905) Create a new `x/consensus` module that is now responsible for maintaining Tendermint consensus parameters instead of `x/param`. Legacy types remain in order to facilitate parameter migration from the deprecated `x/params`. App developers should ensure that they execute `baseapp.MigrateParams` during their chain upgrade. These legacy types will be removed in a future release.
* (x/auth) [#13612](https://github.com/cosmos/cosmos-sdk/pull/13612) Add `Query/ModuleAccountByName` endpoint for accessing the module account info by module name.

### Improvements

Expand Down
2,559 changes: 1,768 additions & 791 deletions api/cosmos/auth/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions api/cosmos/auth/v1beta1/query_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 27 additions & 11 deletions proto/cosmos/auth/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ service Query {
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts";
}

// ModuleAccountByName returns the module account info by module name
rpc ModuleAccountByName(QueryModuleAccountByNameRequest) returns (QueryModuleAccountByNameResponse) {
option (cosmos.query.v1.module_query_safe) = true;
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts/{name}";
}

// Bech32Prefix queries bech32Prefix
//
// Since: cosmos-sdk 0.46
Expand Down Expand Up @@ -114,17 +120,6 @@ message QueryAccountRequest {
string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method.
//
// Since: cosmos-sdk 0.46
message QueryModuleAccountsRequest {}

// QueryParamsResponse is the response type for the Query/Params RPC method.
message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [(gogoproto.nullable) = false];
}

// QueryAccountResponse is the response type for the Query/Account RPC method.
message QueryAccountResponse {
// account defines the account of the corresponding address.
Expand All @@ -134,13 +129,34 @@ message QueryAccountResponse {
// QueryParamsRequest is the request type for the Query/Params RPC method.
message QueryParamsRequest {}

// QueryParamsResponse is the response type for the Query/Params RPC method.
message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [(gogoproto.nullable) = false];
}

// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method.
//
// Since: cosmos-sdk 0.46
message QueryModuleAccountsRequest {}

// QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method.
//
// Since: cosmos-sdk 0.46
message QueryModuleAccountsResponse {
repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"];
}

// QueryModuleAccountByNameRequest is the request type for the Query/ModuleAccountByName RPC method.
message QueryModuleAccountByNameRequest {
string name = 1;
}

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

// Bech32PrefixRequest is the request type for Bech32Prefix rpc method.
//
// Since: cosmos-sdk 0.46
Expand Down
63 changes: 63 additions & 0 deletions tests/e2e/auth/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"cosmossdk.io/math"

authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil"
"github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/cosmos/cosmos-sdk/client"

Expand Down Expand Up @@ -1422,6 +1423,68 @@ func (s *IntegrationTestSuite) TestGetAccountsCmd() {
s.Require().NotEmpty(res.Accounts)
}

func (s *IntegrationTestSuite) TestQueryModuleAccountByNameCmd() {
val := s.network.Validators[0]

testCases := []struct {
name string
moduleName string
expectErr bool
}{
{
"invalid module name",
"gover",
true,
},
{
"valid module name",
"mint",
false,
},
}

for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
clientCtx := val.ClientCtx

out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.QueryModuleAccountByNameCmd(), []string{
tc.moduleName,
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
})
if tc.expectErr {
s.Require().Error(err)
s.Require().NotEqual("internal", err.Error())
} else {
var res authtypes.QueryModuleAccountByNameResponse
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res))

var account types.AccountI
err := val.ClientCtx.InterfaceRegistry.UnpackAny(res.Account, &account)
s.Require().NoError(err)

moduleAccount, ok := account.(types.ModuleAccountI)
s.Require().True(ok)
s.Require().Equal(tc.moduleName, moduleAccount.GetName())
}
})
}
}

func (s *IntegrationTestSuite) TestQueryModuleAccountsCmd() {
val := s.network.Validators[0]
clientCtx := val.ClientCtx

out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.QueryModuleAccountsCmd(), []string{
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
})
s.Require().NoError(err)

var res authtypes.QueryModuleAccountsResponse
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res))
s.Require().NotEmpty(res.Accounts)
}

func TestGetBroadcastCommandOfflineFlag(t *testing.T) {
cmd := authcli.GetBroadcastCommand()
_ = testutil.ApplyMockIODiscardOutErr(cmd)
Expand Down
35 changes: 35 additions & 0 deletions x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func GetQueryCmd() *cobra.Command {
GetAccountsCmd(),
QueryParamsCmd(),
QueryModuleAccountsCmd(),
QueryModuleAccountByNameCmd(),
)

return cmd
Expand Down Expand Up @@ -219,6 +220,40 @@ func QueryModuleAccountsCmd() *cobra.Command {
return cmd
}

// QueryModuleAccountByNameCmd returns a command to
func QueryModuleAccountByNameCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "module-account [module-name]",
Short: "Query module account info by module name",
Args: cobra.ExactArgs(1),
Example: fmt.Sprintf("%s q auth module-account auth", version.AppName),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

moduleName := args[0]
if len(moduleName) == 0 {
return fmt.Errorf("module name should not be empty")
}

queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.ModuleAccountByName(context.Background(), &types.QueryModuleAccountByNameRequest{Name: moduleName})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// QueryTxsByEventsCmd returns a command to search through transactions by events.
func QueryTxsByEventsCmd() *cobra.Command {
cmd := &cobra.Command{
Expand Down
25 changes: 25 additions & 0 deletions x/auth/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,31 @@ func (ak AccountKeeper) ModuleAccounts(c context.Context, req *types.QueryModule
return &types.QueryModuleAccountsResponse{Accounts: modAccounts}, nil
}

// ModuleAccountByName returns module account by module name
func (ak AccountKeeper) ModuleAccountByName(c context.Context, req *types.QueryModuleAccountByNameRequest) (*types.QueryModuleAccountByNameResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

if len(req.Name) == 0 {
return nil, status.Error(codes.InvalidArgument, "module name is empty")
}

ctx := sdk.UnwrapSDKContext(c)
moduleName := req.Name

account := ak.GetModuleAccount(ctx, moduleName)
if account == nil {
return nil, status.Errorf(codes.NotFound, "account %s not found", moduleName)
}
any, err := codectypes.NewAnyWithValue(account)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}

return &types.QueryModuleAccountByNameResponse{Account: any}, nil
}

// Bech32Prefix returns the keeper internally stored bech32 prefix.
func (ak AccountKeeper) Bech32Prefix(ctx context.Context, req *types.Bech32PrefixRequest) (*types.Bech32PrefixResponse, error) {
bech32Prefix, err := ak.getBech32Prefix()
Expand Down
55 changes: 55 additions & 0 deletions x/auth/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,61 @@ func (suite *KeeperTestSuite) TestGRPCQueryModuleAccounts() {
}
}

func (suite *KeeperTestSuite) TestGRPCQueryModuleAccountByName() {
var req *types.QueryModuleAccountByNameRequest

testCases := []struct {
msg string
malleate func()
expPass bool
posttests func(res *types.QueryModuleAccountByNameResponse)
}{
{
"success",
func() {
req = &types.QueryModuleAccountByNameRequest{Name: "mint"}
},
true,
func(res *types.QueryModuleAccountByNameResponse) {
var account types.AccountI
err := suite.encCfg.InterfaceRegistry.UnpackAny(res.Account, &account)
suite.Require().NoError(err)

moduleAccount, ok := account.(types.ModuleAccountI)
suite.Require().True(ok)
suite.Require().Equal(moduleAccount.GetName(), "mint")
},
},
{
"invalid module name",
func() {
req = &types.QueryModuleAccountByNameRequest{Name: "gover"}
},
false,
func(res *types.QueryModuleAccountByNameResponse) {
},
},
}

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.ModuleAccountByName(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) TestBech32Prefix() {
suite.SetupTest() // reset
req := &types.Bech32PrefixRequest{}
Expand Down
Loading

0 comments on commit ddf5cf0

Please sign in to comment.