Skip to content

Commit

Permalink
feat: Add cli for listing all module accounts (#242)
Browse files Browse the repository at this point in the history
cref: cosmos#9812

## Description

This PR adds a CLI for querying all module accounts, `module-accounts`, in the auth module. 
Using this command would display all the account information, including human readable name of the module, module account address, account number, permissions, etc. 

This command would be especially useful for developers using cosmos-sdk as there are lots of instances where a developer would have to look into the module account, but only to do so by getting the module account address after getting the module name, hashing it and bech32fying it in the status quo. By using this command, users would be able to get a quick look on the list of all module accounts' information.


---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [x] reviewed tests and test coverage
- [x] manually tested (if applicable)
  • Loading branch information
mattverse authored May 25, 2022
1 parent 9a05fa0 commit 889979c
Show file tree
Hide file tree
Showing 7 changed files with 617 additions and 35 deletions.
28 changes: 28 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
- [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse)
- [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest)
- [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse)
- [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest)
- [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse)
- [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest)
- [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse)

Expand Down Expand Up @@ -840,6 +842,31 @@ Since: cosmos-sdk 0.43



<a name="cosmos.auth.v1beta1.QueryModuleAccountsRequest"></a>

### QueryModuleAccountsRequest
QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method.






<a name="cosmos.auth.v1beta1.QueryModuleAccountsResponse"></a>

### QueryModuleAccountsResponse
QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `accounts` | [google.protobuf.Any](#google.protobuf.Any) | repeated | |






<a name="cosmos.auth.v1beta1.QueryParamsRequest"></a>

### QueryParamsRequest
Expand Down Expand Up @@ -883,6 +910,7 @@ Query defines the gRPC querier service.
Since: cosmos-sdk 0.43 | GET|/cosmos/auth/v1beta1/accounts|
| `Account` | [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) | [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) | Account returns account details based on address. | GET|/cosmos/auth/v1beta1/accounts/{address}|
| `Params` | [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) | Params queries all parameters. | GET|/cosmos/auth/v1beta1/params|
| `ModuleAccounts` | [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest) | [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse) | ModuleAccounts returns all the existing Module Accounts. | GET|/cosmos/auth/v1beta1/module_accounts|

<!-- end services -->

Expand Down
13 changes: 13 additions & 0 deletions proto/cosmos/auth/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ service Query {
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/cosmos/auth/v1beta1/params";
}

// ModuleAccounts returns all the existing module accounts.
rpc ModuleAccounts(QueryModuleAccountsRequest) returns (QueryModuleAccountsResponse) {
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts";
}
}

// QueryAccountsRequest is the request type for the Query/Accounts RPC method.
Expand Down Expand Up @@ -72,3 +77,11 @@ 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.
message QueryModuleAccountsRequest {}

// QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method.
message QueryModuleAccountsResponse {
repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"];
}
29 changes: 29 additions & 0 deletions x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"fmt"
"strings"

Expand Down Expand Up @@ -43,6 +44,7 @@ func GetQueryCmd() *cobra.Command {
GetAccountCmd(),
GetAccountsCmd(),
QueryParamsCmd(),
QueryModuleAccountsCmd(),
)

return cmd
Expand Down Expand Up @@ -143,6 +145,33 @@ func GetAccountsCmd() *cobra.Command {
return cmd
}

// QueryAllModuleAccountsCmd returns a list of all the existing module accounts with their account information and permissions
func QueryModuleAccountsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "module-accounts",
Short: "Query all module accounts",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.ModuleAccounts(context.Background(), &types.QueryModuleAccountsRequest{})
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 @@ -83,3 +83,28 @@ func (ak AccountKeeper) Params(c context.Context, req *types.QueryParamsRequest)

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

// ModuleAccounts returns all the existing Module Accounts
func (ak AccountKeeper) ModuleAccounts(c context.Context, req *types.QueryModuleAccountsRequest) (*types.QueryModuleAccountsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)

modAccounts := make([]*codectypes.Any, 0, len(ak.permAddrs))

for moduleName := range ak.permAddrs {
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())
}
modAccounts = append(modAccounts, any)
}

return &types.QueryModuleAccountsResponse{Accounts: modAccounts}, nil
}
81 changes: 81 additions & 0 deletions x/auth/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,84 @@ func (suite *KeeperTestSuite) TestGRPCQueryParameters() {
})
}
}

func (suite *KeeperTestSuite) TestGRPCQueryModuleAccounts() {
var (
req *types.QueryModuleAccountsRequest
)

testCases := []struct {
msg string
malleate func()
expPass bool
posttests func(res *types.QueryModuleAccountsResponse)
}{
{
"success",
func() {
req = &types.QueryModuleAccountsRequest{}
},
true,
func(res *types.QueryModuleAccountsResponse) {
var mintModuleExists = false
for _, acc := range res.Accounts {
var account types.AccountI
err := suite.app.InterfaceRegistry().UnpackAny(acc, &account)
suite.Require().NoError(err)

moduleAccount, ok := account.(types.ModuleAccountI)

suite.Require().True(ok)
if moduleAccount.GetName() == "mint" {
mintModuleExists = true
}
}
suite.Require().True(mintModuleExists)
},
},
{
"invalid module name",
func() {
req = &types.QueryModuleAccountsRequest{}
},
true,
func(res *types.QueryModuleAccountsResponse) {
var mintModuleExists = false
for _, acc := range res.Accounts {
var account types.AccountI
err := suite.app.InterfaceRegistry().UnpackAny(acc, &account)
suite.Require().NoError(err)

moduleAccount, ok := account.(types.ModuleAccountI)

suite.Require().True(ok)
if moduleAccount.GetName() == "falseCase" {
mintModuleExists = true
}
}
suite.Require().False(mintModuleExists)
},
},
}

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.ModuleAccounts(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)
})
}
}
Loading

0 comments on commit 889979c

Please sign in to comment.