Skip to content

Commit ddf5cf0

Browse files
authored
feat(cli): add module-account cli cmd and grpc get api (cosmos#13612)
1 parent e279271 commit ddf5cf0

File tree

10 files changed

+2717
-1013
lines changed

10 files changed

+2717
-1013
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
5353
* (x/bank) [#11981](https://github.com/cosmos/cosmos-sdk/pull/11981) Create the `SetSendEnabled` endpoint for managing the bank's SendEnabled settings.
5454
* (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info.
5555
* (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.
56+
* (x/auth) [#13612](https://github.com/cosmos/cosmos-sdk/pull/13612) Add `Query/ModuleAccountByName` endpoint for accessing the module account info by module name.
5657

5758
### Improvements
5859

api/cosmos/auth/v1beta1/query.pulsar.go

Lines changed: 1768 additions & 791 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/cosmos/auth/v1beta1/query_grpc.pb.go

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/cosmos/auth/v1beta1/query.proto

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ service Query {
5252
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts";
5353
}
5454

55+
// ModuleAccountByName returns the module account info by module name
56+
rpc ModuleAccountByName(QueryModuleAccountByNameRequest) returns (QueryModuleAccountByNameResponse) {
57+
option (cosmos.query.v1.module_query_safe) = true;
58+
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts/{name}";
59+
}
60+
5561
// Bech32Prefix queries bech32Prefix
5662
//
5763
// Since: cosmos-sdk 0.46
@@ -114,17 +120,6 @@ message QueryAccountRequest {
114120
string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
115121
}
116122

117-
// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method.
118-
//
119-
// Since: cosmos-sdk 0.46
120-
message QueryModuleAccountsRequest {}
121-
122-
// QueryParamsResponse is the response type for the Query/Params RPC method.
123-
message QueryParamsResponse {
124-
// params defines the parameters of the module.
125-
Params params = 1 [(gogoproto.nullable) = false];
126-
}
127-
128123
// QueryAccountResponse is the response type for the Query/Account RPC method.
129124
message QueryAccountResponse {
130125
// account defines the account of the corresponding address.
@@ -134,13 +129,34 @@ message QueryAccountResponse {
134129
// QueryParamsRequest is the request type for the Query/Params RPC method.
135130
message QueryParamsRequest {}
136131

132+
// QueryParamsResponse is the response type for the Query/Params RPC method.
133+
message QueryParamsResponse {
134+
// params defines the parameters of the module.
135+
Params params = 1 [(gogoproto.nullable) = false];
136+
}
137+
138+
// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method.
139+
//
140+
// Since: cosmos-sdk 0.46
141+
message QueryModuleAccountsRequest {}
142+
137143
// QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method.
138144
//
139145
// Since: cosmos-sdk 0.46
140146
message QueryModuleAccountsResponse {
141147
repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"];
142148
}
143149

150+
// QueryModuleAccountByNameRequest is the request type for the Query/ModuleAccountByName RPC method.
151+
message QueryModuleAccountByNameRequest {
152+
string name = 1;
153+
}
154+
155+
// QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method.
156+
message QueryModuleAccountByNameResponse {
157+
google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"];
158+
}
159+
144160
// Bech32PrefixRequest is the request type for Bech32Prefix rpc method.
145161
//
146162
// Since: cosmos-sdk 0.46

tests/e2e/auth/suite.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"cosmossdk.io/math"
1919

2020
authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil"
21+
"github.com/cosmos/cosmos-sdk/x/auth/types"
2122

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

@@ -1422,6 +1423,68 @@ func (s *IntegrationTestSuite) TestGetAccountsCmd() {
14221423
s.Require().NotEmpty(res.Accounts)
14231424
}
14241425

1426+
func (s *IntegrationTestSuite) TestQueryModuleAccountByNameCmd() {
1427+
val := s.network.Validators[0]
1428+
1429+
testCases := []struct {
1430+
name string
1431+
moduleName string
1432+
expectErr bool
1433+
}{
1434+
{
1435+
"invalid module name",
1436+
"gover",
1437+
true,
1438+
},
1439+
{
1440+
"valid module name",
1441+
"mint",
1442+
false,
1443+
},
1444+
}
1445+
1446+
for _, tc := range testCases {
1447+
tc := tc
1448+
s.Run(tc.name, func() {
1449+
clientCtx := val.ClientCtx
1450+
1451+
out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.QueryModuleAccountByNameCmd(), []string{
1452+
tc.moduleName,
1453+
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
1454+
})
1455+
if tc.expectErr {
1456+
s.Require().Error(err)
1457+
s.Require().NotEqual("internal", err.Error())
1458+
} else {
1459+
var res authtypes.QueryModuleAccountByNameResponse
1460+
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res))
1461+
1462+
var account types.AccountI
1463+
err := val.ClientCtx.InterfaceRegistry.UnpackAny(res.Account, &account)
1464+
s.Require().NoError(err)
1465+
1466+
moduleAccount, ok := account.(types.ModuleAccountI)
1467+
s.Require().True(ok)
1468+
s.Require().Equal(tc.moduleName, moduleAccount.GetName())
1469+
}
1470+
})
1471+
}
1472+
}
1473+
1474+
func (s *IntegrationTestSuite) TestQueryModuleAccountsCmd() {
1475+
val := s.network.Validators[0]
1476+
clientCtx := val.ClientCtx
1477+
1478+
out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.QueryModuleAccountsCmd(), []string{
1479+
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
1480+
})
1481+
s.Require().NoError(err)
1482+
1483+
var res authtypes.QueryModuleAccountsResponse
1484+
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res))
1485+
s.Require().NotEmpty(res.Accounts)
1486+
}
1487+
14251488
func TestGetBroadcastCommandOfflineFlag(t *testing.T) {
14261489
cmd := authcli.GetBroadcastCommand()
14271490
_ = testutil.ApplyMockIODiscardOutErr(cmd)

x/auth/client/cli/query.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func GetQueryCmd() *cobra.Command {
4646
GetAccountsCmd(),
4747
QueryParamsCmd(),
4848
QueryModuleAccountsCmd(),
49+
QueryModuleAccountByNameCmd(),
4950
)
5051

5152
return cmd
@@ -219,6 +220,40 @@ func QueryModuleAccountsCmd() *cobra.Command {
219220
return cmd
220221
}
221222

223+
// QueryModuleAccountByNameCmd returns a command to
224+
func QueryModuleAccountByNameCmd() *cobra.Command {
225+
cmd := &cobra.Command{
226+
Use: "module-account [module-name]",
227+
Short: "Query module account info by module name",
228+
Args: cobra.ExactArgs(1),
229+
Example: fmt.Sprintf("%s q auth module-account auth", version.AppName),
230+
RunE: func(cmd *cobra.Command, args []string) error {
231+
clientCtx, err := client.GetClientQueryContext(cmd)
232+
if err != nil {
233+
return err
234+
}
235+
236+
moduleName := args[0]
237+
if len(moduleName) == 0 {
238+
return fmt.Errorf("module name should not be empty")
239+
}
240+
241+
queryClient := types.NewQueryClient(clientCtx)
242+
243+
res, err := queryClient.ModuleAccountByName(context.Background(), &types.QueryModuleAccountByNameRequest{Name: moduleName})
244+
if err != nil {
245+
return err
246+
}
247+
248+
return clientCtx.PrintProto(res)
249+
},
250+
}
251+
252+
flags.AddQueryFlagsToCmd(cmd)
253+
254+
return cmd
255+
}
256+
222257
// QueryTxsByEventsCmd returns a command to search through transactions by events.
223258
func QueryTxsByEventsCmd() *cobra.Command {
224259
cmd := &cobra.Command{

x/auth/keeper/grpc_query.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,31 @@ func (ak AccountKeeper) ModuleAccounts(c context.Context, req *types.QueryModule
135135
return &types.QueryModuleAccountsResponse{Accounts: modAccounts}, nil
136136
}
137137

138+
// ModuleAccountByName returns module account by module name
139+
func (ak AccountKeeper) ModuleAccountByName(c context.Context, req *types.QueryModuleAccountByNameRequest) (*types.QueryModuleAccountByNameResponse, error) {
140+
if req == nil {
141+
return nil, status.Errorf(codes.InvalidArgument, "empty request")
142+
}
143+
144+
if len(req.Name) == 0 {
145+
return nil, status.Error(codes.InvalidArgument, "module name is empty")
146+
}
147+
148+
ctx := sdk.UnwrapSDKContext(c)
149+
moduleName := req.Name
150+
151+
account := ak.GetModuleAccount(ctx, moduleName)
152+
if account == nil {
153+
return nil, status.Errorf(codes.NotFound, "account %s not found", moduleName)
154+
}
155+
any, err := codectypes.NewAnyWithValue(account)
156+
if err != nil {
157+
return nil, status.Errorf(codes.Internal, err.Error())
158+
}
159+
160+
return &types.QueryModuleAccountByNameResponse{Account: any}, nil
161+
}
162+
138163
// Bech32Prefix returns the keeper internally stored bech32 prefix.
139164
func (ak AccountKeeper) Bech32Prefix(ctx context.Context, req *types.Bech32PrefixRequest) (*types.Bech32PrefixResponse, error) {
140165
bech32Prefix, err := ak.getBech32Prefix()

x/auth/keeper/grpc_query_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,61 @@ func (suite *KeeperTestSuite) TestGRPCQueryModuleAccounts() {
350350
}
351351
}
352352

353+
func (suite *KeeperTestSuite) TestGRPCQueryModuleAccountByName() {
354+
var req *types.QueryModuleAccountByNameRequest
355+
356+
testCases := []struct {
357+
msg string
358+
malleate func()
359+
expPass bool
360+
posttests func(res *types.QueryModuleAccountByNameResponse)
361+
}{
362+
{
363+
"success",
364+
func() {
365+
req = &types.QueryModuleAccountByNameRequest{Name: "mint"}
366+
},
367+
true,
368+
func(res *types.QueryModuleAccountByNameResponse) {
369+
var account types.AccountI
370+
err := suite.encCfg.InterfaceRegistry.UnpackAny(res.Account, &account)
371+
suite.Require().NoError(err)
372+
373+
moduleAccount, ok := account.(types.ModuleAccountI)
374+
suite.Require().True(ok)
375+
suite.Require().Equal(moduleAccount.GetName(), "mint")
376+
},
377+
},
378+
{
379+
"invalid module name",
380+
func() {
381+
req = &types.QueryModuleAccountByNameRequest{Name: "gover"}
382+
},
383+
false,
384+
func(res *types.QueryModuleAccountByNameResponse) {
385+
},
386+
},
387+
}
388+
389+
for _, tc := range testCases {
390+
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
391+
suite.SetupTest() // reset
392+
tc.malleate()
393+
ctx := sdk.WrapSDKContext(suite.ctx)
394+
res, err := suite.queryClient.ModuleAccountByName(ctx, req)
395+
if tc.expPass {
396+
suite.Require().NoError(err)
397+
suite.Require().NotNil(res)
398+
} else {
399+
suite.Require().Error(err)
400+
suite.Require().Nil(res)
401+
}
402+
403+
tc.posttests(res)
404+
})
405+
}
406+
}
407+
353408
func (suite *KeeperTestSuite) TestBech32Prefix() {
354409
suite.SetupTest() // reset
355410
req := &types.Bech32PrefixRequest{}

0 commit comments

Comments
 (0)