Skip to content

Commit

Permalink
feat: authz add query all grants by granter (cosmos#10326)
Browse files Browse the repository at this point in the history
<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

Closes: cosmos#9966 

<!-- Add a description of the changes that this PR introduces and the files that
are the most critical to review. -->

---

### 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...

- [x] 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
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] 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...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] 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
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
aleem1314 authored Oct 11, 2021
1 parent 7e6151e commit 3637270
Show file tree
Hide file tree
Showing 10 changed files with 1,020 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes.
* [\#9837](https://github.com/cosmos/cosmos-sdk/issues/9837) `--generate-only` flag will accept the keyname now.
* [\#10045](https://github.com/cosmos/cosmos-sdk/pull/10045) Revert [#8549](https://github.com/cosmos/cosmos-sdk/pull/8549). Do not route grpc queries through Tendermint.
* [\#10326](https://github.com/cosmos/cosmos-sdk/pull/10326) `x/authz` add query all grants by granter query.

### API Breaking Changes

Expand Down
35 changes: 35 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
- [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization)

- [cosmos/authz/v1beta1/query.proto](#cosmos/authz/v1beta1/query.proto)
- [QueryGranterGrantsRequest](#cosmos.authz.v1beta1.QueryGranterGrantsRequest)
- [QueryGranterGrantsResponse](#cosmos.authz.v1beta1.QueryGranterGrantsResponse)
- [QueryGrantsRequest](#cosmos.authz.v1beta1.QueryGrantsRequest)
- [QueryGrantsResponse](#cosmos.authz.v1beta1.QueryGrantsResponse)

Expand Down Expand Up @@ -1274,6 +1276,38 @@ GrantAuthorization defines the GenesisState/GrantAuthorization type.



<a name="cosmos.authz.v1beta1.QueryGranterGrantsRequest"></a>

### QueryGranterGrantsRequest
QueryGranterGrantsRequest is the request type for the Query/Grants RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `granter` | [string](#string) | | |
| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. |






<a name="cosmos.authz.v1beta1.QueryGranterGrantsResponse"></a>

### QueryGranterGrantsResponse
QueryGranterGrantsResponse is the response type for the Query/GranterGrants RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `grants` | [Grant](#cosmos.authz.v1beta1.Grant) | repeated | authorizations is a list of grants granted for grantee by granter. |
| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. |






<a name="cosmos.authz.v1beta1.QueryGrantsRequest"></a>

### QueryGrantsRequest
Expand Down Expand Up @@ -1322,6 +1356,7 @@ Query defines the gRPC querier service.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Grants` | [QueryGrantsRequest](#cosmos.authz.v1beta1.QueryGrantsRequest) | [QueryGrantsResponse](#cosmos.authz.v1beta1.QueryGrantsResponse) | Returns list of `Authorization`, granted to the grantee by the granter. | GET|/cosmos/authz/v1beta1/grants|
| `GranterGrants` | [QueryGranterGrantsRequest](#cosmos.authz.v1beta1.QueryGranterGrantsRequest) | [QueryGranterGrantsResponse](#cosmos.authz.v1beta1.QueryGranterGrantsResponse) | GranterGrants returns list of `Authorization`, granted by granter. | GET|/cosmos/authz/v1beta1/grants/{granter}|

<!-- end services -->

Expand Down
21 changes: 21 additions & 0 deletions proto/cosmos/authz/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ service Query {
rpc Grants(QueryGrantsRequest) returns (QueryGrantsResponse) {
option (google.api.http).get = "/cosmos/authz/v1beta1/grants";
}

// GranterGrants returns list of `Authorization`, granted by granter.
rpc GranterGrants(QueryGranterGrantsRequest) returns (QueryGranterGrantsResponse) {
option (google.api.http).get = "/cosmos/authz/v1beta1/grants/{granter}";
}
}

// QueryGrantsRequest is the request type for the Query/Grants RPC method.
Expand All @@ -33,3 +38,19 @@ message QueryGrantsResponse {
// pagination defines an pagination for the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryGranterGrantsRequest is the request type for the Query/GranterGrants RPC method.
message QueryGranterGrantsRequest {
string granter = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// pagination defines an pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

// QueryGranterGrantsResponse is the response type for the Query/GranterGrants RPC method.
message QueryGranterGrantsResponse {
// authorizations is a list of grants granted for grantee by granter.
repeated cosmos.authz.v1beta1.Grant grants = 1;
// pagination defines an pagination for the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
49 changes: 49 additions & 0 deletions x/authz/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func GetQueryCmd() *cobra.Command {

authorizationQueryCmd.AddCommand(
GetCmdQueryGrants(),
GetQueryGranterGrants(),
)

return authorizationQueryCmd
Expand Down Expand Up @@ -91,3 +92,51 @@ $ %s query %s grants cosmos1skjw.. cosmos1skjwj.. %s
flags.AddPaginationFlagsToCmd(cmd, "grants")
return cmd
}

func GetQueryGranterGrants() *cobra.Command {
cmd := &cobra.Command{
Use: "granter-grants [granter-addr]",
Args: cobra.ExactArgs(1),
Short: "query authorization grants granted by granter",
Long: strings.TrimSpace(
fmt.Sprintf(`Query authorization grants granted by granter.
Examples:
$ %s q %s granter-grants cosmos1skj..
`,
version.AppName, authz.ModuleName),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

granter, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}

queryClient := authz.NewQueryClient(clientCtx)
res, err := queryClient.GranterGrants(
cmd.Context(),
&authz.QueryGranterGrantsRequest{
Granter: granter.String(),
Pagination: pageReq,
},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "granter-grants")
return cmd
}
57 changes: 57 additions & 0 deletions x/authz/client/testutil/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,60 @@ func (s *IntegrationTestSuite) TestQueryGrantsGRPC() {
})
}
}

func (s *IntegrationTestSuite) TestQueryGranterGrantsGRPC() {
val := s.network.Validators[0]
grantee := s.grantee[1]
require := s.Require()

testCases := []struct {
name string
url string
expectErr bool
errMsg string
numItems int
}{
{
"invalid account address",
fmt.Sprintf("%s/cosmos/authz/v1beta1/grants/%s", val.APIAddress, "invalid address"),
true,
"decoding bech32 failed",
0,
},
{
"no authorizations found",
fmt.Sprintf("%s/cosmos/authz/v1beta1/grants/%s", val.APIAddress, grantee.String()),
false,
"",
0,
},
{
"valid query",
fmt.Sprintf("%s/cosmos/authz/v1beta1/grants/%s", val.APIAddress, val.Address.String()),
false,
"",
6,
},
{
"valid query: expect two grants",
fmt.Sprintf("%s/cosmos/authz/v1beta1/grants/%s", val.APIAddress, val.Address.String()),
false,
"",
6,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
resp, _ := rest.GetRequest(tc.url)
if tc.expectErr {
require.Contains(string(resp), tc.errMsg)
} else {
var authorizations authz.QueryGrantsResponse
err := val.ClientCtx.Codec.UnmarshalJSON(resp, &authorizations)
require.NoError(err)
require.Len(authorizations.Grants, tc.numItems)
}

})
}
}
72 changes: 72 additions & 0 deletions x/authz/client/testutil/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,75 @@ func (s *IntegrationTestSuite) TestQueryAuthorization() {
})
}
}

func (s *IntegrationTestSuite) TestQueryGranterGrants() {
val := s.network.Validators[0]
grantee := s.grantee[0]
require := s.Require()

testCases := []struct {
name string
args []string
expectErr bool
expectedErr string
expItems int
}{
{
"invalid address",
[]string{
"invalid-address",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true,
"decoding bech32 failed",
0,
},
{
"no authorization found",
[]string{
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
"",
0,
},
{
"valid case",
[]string{
val.Address.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
"",
6,
},
{
"valid case with pagination",
[]string{
val.Address.String(),
"--limit=2",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false,
"",
2,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := cli.GetQueryGranterGrants()
clientCtx := val.ClientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
require.Error(err)
require.Contains(out.String(), tc.expectedErr)
} else {
require.NoError(err)
var grants authz.QueryGranterGrantsResponse
require.NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &grants))
require.Len(grants.Grants, tc.expItems)
}
})
}
}
47 changes: 47 additions & 0 deletions x/authz/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,53 @@ func (k Keeper) Grants(c context.Context, req *authz.QueryGrantsRequest) (*authz
}, nil
}

// GranterGrants implements the Query/GranterGrants gRPC method.
func (k Keeper) GranterGrants(c context.Context, req *authz.QueryGranterGrantsRequest) (*authz.QueryGranterGrantsResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

granter, err := sdk.AccAddressFromBech32(req.Granter)
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(c)
store := ctx.KVStore(k.storeKey)
authzStore := prefix.NewStore(store, grantStoreKey(nil, granter, ""))

var authorizations []*authz.Grant
pageRes, err := query.FilteredPaginate(authzStore, req.Pagination, func(key []byte, value []byte,
accumulate bool) (bool, error) {
auth, err := unmarshalAuthorization(k.cdc, value)
if err != nil {
return false, err
}

auth1 := auth.GetAuthorization()
if accumulate {
any, err := codectypes.NewAnyWithValue(auth1)
if err != nil {
return false, status.Errorf(codes.Internal, err.Error())
}

authorizations = append(authorizations, &authz.Grant{
Authorization: any,
Expiration: auth.Expiration,
})
}
return true, nil
})
if err != nil {
return nil, err
}

return &authz.QueryGranterGrantsResponse{
Grants: authorizations,
Pagination: pageRes,
}, nil
}

// unmarshal an authorization from a store value
func unmarshalAuthorization(cdc codec.BinaryCodec, value []byte) (v authz.Grant, err error) {
err = cdc.Unmarshal(value, &v)
Expand Down
Loading

0 comments on commit 3637270

Please sign in to comment.