Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): add module-account cli cmd and grpc get api #13612

Merged
merged 8 commits into from Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
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,563 changes: 1,772 additions & 791 deletions api/cosmos/auth/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

42 changes: 42 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.

44 changes: 33 additions & 11 deletions proto/cosmos/auth/v1beta1/query.proto
Expand Up @@ -52,6 +52,14 @@ service Query {
option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts";
}

// ModuleAccountByName returns the module account info by module name
//
// Since: cosmos-sdk 0.47
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
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 +122,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 +131,38 @@ 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.
//
// Since: cosmos-sdk 0.47
message QueryModuleAccountByNameRequest {
string name = 1;
}

// QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method.
//
// Since: cosmos-sdk 0.47
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
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
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
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
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