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(bank): Add secondary query for bank metadata #16852

Merged
merged 11 commits into from
Jul 27, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/distribution) [#16607](https://github.com/cosmos/cosmos-sdk/pull/16607) use collections for `ValidatorHistoricalRewards` state management:
* remove `Keeper`: `IterateValidatorHistoricalRewards`, `GetValidatorHistoricalRewards`, `SetValidatorHistoricalRewards`, `DeleteValidatorHistoricalRewards`, `DeleteValidatorHistoricalReward`, `DeleteAllValidatorHistoricalRewards`
* (x/staking) [#16795](https://github.com/cosmos/cosmos-sdk/pull/16795) `DelegationToDelegationResponse`, `DelegationsToDelegationResponses`, `RedelegationsToRedelegationResponses` are no longer exported.
* (x/module) [#16795](https://github.com/cosmos/cosmos-sdk/pull/16852) Add `DenomMetadataByQueryString` query in bank module to support metadata query by query string.
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
* [#16441](https://github.com/cosmos/cosmos-sdk/pull/16441) Params state is migrated to collections. `GetParams` has been removed

## [v0.50.0-alpha.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.0-alpha.1) - 2023-06-30
Expand Down
4 changes: 2 additions & 2 deletions api/cosmos/bank/module/v1/module.pulsar.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how come this has changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I do not know, probably something to do with protoc or how we generate pulsar files, do you want me to revert this file for now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just running make proto-gen should suffice.

Copy link
Contributor Author

@mattverse mattverse Jul 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have used the same command for proto gen 🤔 I'm wondering if we check docker image versions for protogen and if that might be the cause for this?

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

1,515 changes: 1,250 additions & 265 deletions api/cosmos/bank/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

61 changes: 50 additions & 11 deletions api/cosmos/bank/v1beta1/query_grpc.pb.go

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

20 changes: 20 additions & 0 deletions proto/cosmos/bank/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ service Query {
option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata/{denom}";
}

// DenomsMetadata queries the client metadata of a given coin denomination.
rpc DenomMetadataByQueryString(QueryDenomMetadataByQueryStringRequest)
returns (QueryDenomMetadataByQueryStringResponse) {
option (cosmos.query.v1.module_query_safe) = true;
option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata_by_query_string";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata_by_query_string";
option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata_by_query";

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, query string seems like the correct term to use, is there a reason for suggestion above?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a few items:

  • It makes an already verbose query even more verbose without adding context
  • The fact that denom is a string is already available based on the proto schema
  • Generally, you dont wan't to annotate a query's path (e.g. /users/by_name vs /users/by_name_string), the latter doesn't give you anything as it's superfluous IMO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I was referring to query string as a single term here though (https://en.wikipedia.org/wiki/Query_string)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with it either way, but just wanted to make things clear :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker for me, just my two cents :p

we can merge

}
// DenomsMetadata queries the client metadata for all registered coin
// denominations.
rpc DenomsMetadata(QueryDenomsMetadataRequest) returns (QueryDenomsMetadataResponse) {
Expand Down Expand Up @@ -300,6 +306,20 @@ message QueryDenomMetadataResponse {
Metadata metadata = 1 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}

// QueryDenomMetadataByQueryStringRequest is the request type for the Query/DenomMetadata RPC method.
// Identical with QueryDenomMetadataRequest but receives denom as query string.
message QueryDenomMetadataByQueryStringRequest {
// denom is the coin denom to query the metadata for.
string denom = 1;
}

// QueryDenomMetadataByQueryStringResponse is the response type for the Query/DenomMetadata RPC
// method. Identical with QueryDenomMetadataResponse but receives denom as query string in request.
message QueryDenomMetadataByQueryStringResponse {
// metadata describes and provides all the client information for the requested token.
Metadata metadata = 1 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}

// QueryDenomOwnersRequest defines the request type for the DenomOwners RPC query,
// which queries for a paginated set of all account holders of a particular
// denomination.
Expand Down
22 changes: 22 additions & 0 deletions x/bank/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,28 @@ func (k BaseKeeper) DenomMetadata(c context.Context, req *types.QueryDenomMetada
}, nil
}

// DenomMetadataByQueryString is identical to DenomMetadata query, but receives request via query string.
func (k BaseKeeper) DenomMetadataByQueryString(c context.Context, req *types.QueryDenomMetadataByQueryStringRequest) (*types.QueryDenomMetadataByQueryStringResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missine something but isn't this the same as DenomMetadata?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the issue is just the fact that we MUST provide a denom in the URL path, then I would avoid creating a duplicate query handler -- we can at least extract out the handler code into a single method.

In addition, is there no annotation or something that we can add to the existing query to allow an empty query argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can at least extract out the handler code into a single method.

@alexanderbez We need to suffice the BaseKeeper Interface, thus need to have this method with this specific request field and response field. What I can do is extract the duplicated method between DenomMetadata and DenomMetadataByQueryString` to reduce code duplication.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the code within the query handlers are identical I believe, so that should be kept DRY 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the second look, the way it is right now seems as far as we could go for having non-duplicated code.
We use the same abstracted keeper method, namely k.GetDenomMetaData whilst the remaining part is simply handling the request and response itself

if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

if err := sdk.ValidateDenom(req.Denom); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

ctx := sdk.UnwrapSDKContext(c)

metadata, found := k.GetDenomMetaData(ctx, req.Denom)
if !found {
return nil, status.Errorf(codes.NotFound, "client metadata for denom %s", req.Denom)
}

return &types.QueryDenomMetadataByQueryStringResponse{
Metadata: metadata,
}, nil
}

// DenomMetadataV2 is identical to DenomMetadata but receives protoreflect types instead of gogo types. It exists to
// resolve a cyclic dependency existent between x/auth and x/bank, so that x/auth may call this keeper without
// depending on x/bank.
Expand Down
77 changes: 77 additions & 0 deletions x/bank/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,83 @@ func (suite *KeeperTestSuite) TestQueryDenomMetadataRequest() {
}
}

func (suite *KeeperTestSuite) TestQueryDenomMetadataByQueryStringRequest() {
var (
req *types.QueryDenomMetadataByQueryStringRequest
expMetadata = types.Metadata{}
)

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty denom",
func() {
req = &types.QueryDenomMetadataByQueryStringRequest{}
},
false,
},
{
"not found denom",
func() {
req = &types.QueryDenomMetadataByQueryStringRequest{
Denom: "foo",
}
},
false,
},
{
"success",
func() {
expMetadata = types.Metadata{
Description: "The native staking token of the Cosmos Hub.",
DenomUnits: []*types.DenomUnit{
{
Denom: "uatom",
Exponent: 0,
Aliases: []string{"microatom"},
},
{
Denom: "atom",
Exponent: 6,
Aliases: []string{"ATOM"},
},
},
Base: "uatom",
Display: "atom",
}

suite.bankKeeper.SetDenomMetaData(suite.ctx, expMetadata)
req = &types.QueryDenomMetadataByQueryStringRequest{
Denom: expMetadata.Base,
}
},
true,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
ctx := suite.ctx

res, err := suite.queryClient.DenomMetadataByQueryString(ctx, req)

if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expMetadata, res.Metadata)
} else {
suite.Require().Error(err)
}
})
}
}

func (suite *KeeperTestSuite) TestGRPCDenomOwners() {
ctx := suite.ctx

Expand Down
Loading