Skip to content

Commit

Permalink
feat: resolve IBC hash to human readable denom (#14894)
Browse files Browse the repository at this point in the history
Co-authored-by: Julien Robert <julien@rbrt.fr>
  • Loading branch information
0xmuralik and julienrbrt authored Feb 6, 2023
1 parent 23d9fc4 commit ffde2b9
Show file tree
Hide file tree
Showing 12 changed files with 583 additions and 389 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (client) [#14509](https://github.com/cosmos/cosmos-sdk/pull/#14509) Added `AddKeyringFlags` function.
* (x/gov,cli) [#14718](https://github.com/cosmos/cosmos-sdk/pull/14718) Added `AddGovPropFlagsToCmd` and `ReadGovPropFlags` functions.
* (store) [#14189](https://github.com/cosmos/cosmos-sdk/pull/14189) Add config `iavl-lazy-loading` to enable lazy loading of iavl store, to improve start up time of archive nodes, add method `SetLazyLoading` to `CommitMultiStore` interface.
* (x/bank) [#14894](https://github.com/cosmos/cosmos-sdk/pull/14894) Return a human readable denomination for IBC vouchers when querying bank balances. Added a `ResolveDenom` parameter to `types.QueryAllBalancesRequest` and `--resolve-denom` flag to `GetBalancesCmd()`.
* (x/groups) [#14879](https://github.com/cosmos/cosmos-sdk/pull/14879) Add `Query/Groups` query to get all the groups.

### Improvements
Expand Down Expand Up @@ -247,6 +248,7 @@ extension interfaces. `module.Manager.Modules` is now of type `map[string]interf
* (store/streaming)[#14603](https://github.com/cosmos/cosmos-sdk/pull/14603) `StoreDecoderRegistry` moved from store to `types/simulations` this breaks the `AppModuleSimulation` interface.
* (x/staking) [#14590](https://github.com/cosmos/cosmos-sdk/pull/14590) `MsgUndelegateResponse` now includes undelegated amount. `x/staking` module's `keeper.Undelegate` now returns 3 values (completionTime,undelegateAmount,error) instead of 2.
* (x/feegrant) [14649](https://github.com/cosmos/cosmos-sdk/pull/14649) Extract Feegrant in its own go.mod and rename the package to `cosmossdk.io/x/feegrant`.
* (x/bank) [#14894](https://github.com/cosmos/cosmos-sdk/pull/14894) Return a human readable denomination for IBC vouchers when querying bank balances. Added a `ResolveDenom` parameter to `types.QueryAllBalancesRequest`.

### Client Breaking Changes

Expand Down
617 changes: 342 additions & 275 deletions api/cosmos/bank/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions proto/cosmos/bank/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ message QueryAllBalancesRequest {

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

// resolve_denom is the flag to resolve the denom into a human-readable form from the metadata.
//
// Since: cosmos-sdk 0.48
bool resolve_denom = 3;
}

// QueryAllBalancesResponse is the response type for the Query/AllBalances RPC
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/bank/keeper/deterministic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func TestGRPCQueryAllBalances(t *testing.T) {

fundAccount(f, addr, coins...)

req := banktypes.NewQueryAllBalancesRequest(addr, testdata.PaginationGenerator(rt, uint64(numCoins)).Draw(rt, "pagination"))
req := banktypes.NewQueryAllBalancesRequest(addr, testdata.PaginationGenerator(rt, uint64(numCoins)).Draw(rt, "pagination"), false)
testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.AllBalances, 0, true)
})

Expand All @@ -144,7 +144,7 @@ func TestGRPCQueryAllBalances(t *testing.T) {
)

fundAccount(f, addr1, coins...)
req := banktypes.NewQueryAllBalancesRequest(addr1, nil)
req := banktypes.NewQueryAllBalancesRequest(addr1, nil, false)

testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.AllBalances, 357, false)
}
Expand Down
2 changes: 1 addition & 1 deletion types/query/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func FuzzPagination(f *testing.F) {
}

// Now try to paginate it.
req := types.NewQueryAllBalancesRequest(addr1, qr)
req := types.NewQueryAllBalancesRequest(addr1, qr, false)
balResult := sdk.NewCoins()
authStore := suite.ctx.KVStore(suite.app.UnsafeFindStoreKey(types.StoreKey))
balancesStore := prefix.NewStore(authStore, types.BalancesPrefix)
Expand Down
44 changes: 22 additions & 22 deletions types/query/pagination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify empty page request results a max of defaultLimit records and counts total records")
pageReq := &query.PageRequest{}
request := types.NewQueryAllBalancesRequest(addr1, pageReq)
request := types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err := queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Pagination.Total, uint64(numBalances))
Expand All @@ -129,7 +129,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records")
pageReq = &query.PageRequest{Limit: overLimit}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Pagination.Total, uint64(0))
Expand All @@ -138,7 +138,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with custom limit and countTotal true")
pageReq = &query.PageRequest{Limit: underLimit, CountTotal: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), underLimit)
Expand All @@ -147,7 +147,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with custom limit and countTotal false")
pageReq = &query.PageRequest{Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), defaultLimit)
Expand All @@ -156,7 +156,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with custom limit, key and countTotal false")
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), defaultLimit)
Expand All @@ -165,7 +165,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate for last page, results in records less than max limit")
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().LessOrEqual(res.Balances.Len(), defaultLimit)
Expand All @@ -175,7 +175,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with offset and limit")
pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().LessOrEqual(res.Balances.Len(), defaultLimit)
Expand All @@ -185,7 +185,7 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with offset and limit")
pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().LessOrEqual(res.Balances.Len(), defaultLimit)
Expand All @@ -194,14 +194,14 @@ func (s *paginationTestSuite) TestPagination() {

s.T().Log("verify paginate with offset and key - error")
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
_, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().Error(err)
s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error())

s.T().Log("verify paginate with offset greater than total results")
pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().LessOrEqual(res.Balances.Len(), 0)
Expand All @@ -228,15 +228,15 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with custom limit and countTotal, Reverse false")
pageReq := &query.PageRequest{Limit: 2, CountTotal: true, Reverse: true, Key: nil}
request := types.NewQueryAllBalancesRequest(addr1, pageReq)
request := types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res1, err := queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res1.Balances.Len(), 2)
s.Require().NotNil(res1.Pagination.NextKey)

s.T().Log("verify paginate with custom limit and countTotal, Reverse false")
pageReq = &query.PageRequest{Limit: 150}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res1, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res1.Balances.Len(), 150)
Expand All @@ -245,7 +245,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with custom limit, key and Reverse true")
pageReq = &query.PageRequest{Limit: defaultLimit, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err := queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), defaultLimit)
Expand All @@ -254,7 +254,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with custom limit, key and Reverse true")
pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), defaultLimit)
Expand All @@ -263,7 +263,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate for last page, Reverse true")
pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), lastPageRecords)
Expand All @@ -272,7 +272,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records")
pageReq = &query.PageRequest{Limit: overLimit, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Pagination.Total, uint64(0))
Expand All @@ -281,7 +281,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true")
pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Limit: 50, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), 50)
Expand All @@ -293,7 +293,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true")
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: 50, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), 50)
Expand All @@ -305,7 +305,7 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate for last page Reverse true")
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().Equal(res.Balances.Len(), 51)
Expand All @@ -317,14 +317,14 @@ func (s *paginationTestSuite) TestReversePagination() {

s.T().Log("verify paginate with offset and key - error")
pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
_, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().Error(err)
s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error())

s.T().Log("verify paginate with offset greater than total results")
pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false, Reverse: true}
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
request = types.NewQueryAllBalancesRequest(addr1, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), request)
s.Require().NoError(err)
s.Require().LessOrEqual(res.Balances.Len(), 0)
Expand All @@ -349,7 +349,7 @@ func (s *paginationTestSuite) TestPaginate() {
}
// Paginate example
pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true}
request := types.NewQueryAllBalancesRequest(addr1, pageReq)
request := types.NewQueryAllBalancesRequest(addr1, pageReq, false)
balResult := sdk.NewCoins()
authStore := s.ctx.KVStore(s.app.UnsafeFindStoreKey(types.StoreKey))
balancesStore := prefix.NewStore(authStore, types.BalancesPrefix)
Expand Down
14 changes: 11 additions & 3 deletions x/bank/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import (
)

const (
FlagDenom = "denom"
FlagDenom = "denom"
FlagResolveDenom = "resolve-denom"
)

// GetQueryCmd returns the parent command for all x/bank CLi query commands. The
Expand Down Expand Up @@ -50,8 +51,9 @@ func GetBalancesCmd() *cobra.Command {
Example:
$ %s query %s balances [address]
$ %s query %s balances [address] --denom=[denom]
$ %s query %s balances [address] --resolve-denom
`,
version.AppName, types.ModuleName, version.AppName, types.ModuleName,
version.AppName, types.ModuleName, version.AppName, types.ModuleName, version.AppName, types.ModuleName,
),
),
Args: cobra.ExactArgs(1),
Expand Down Expand Up @@ -81,7 +83,12 @@ Example:
ctx := cmd.Context()

if denom == "" {
params := types.NewQueryAllBalancesRequest(addr, pageReq)
resolveDenom, err := cmd.Flags().GetBool(FlagResolveDenom)
if err != nil {
return err
}

params := types.NewQueryAllBalancesRequest(addr, pageReq, resolveDenom)

res, err := queryClient.AllBalances(ctx, params)
if err != nil {
Expand All @@ -103,6 +110,7 @@ Example:
}

cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for")
cmd.Flags().Bool(FlagResolveDenom, false, "Resolve denom to human-readable denom from metadata")
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "all balances")

Expand Down
9 changes: 9 additions & 0 deletions x/bank/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances

pageRes, err := query.Paginate(accountStore, req.Pagination, func(key, value []byte) error {
denom := string(key)

// IBC denom metadata will be registered in ibc-go after first mint
//
// Since: ibc-go v7
if req.ResolveDenom {
if metadata, ok := k.GetDenomMetaData(sdkCtx, denom); ok {
denom = metadata.Display
}
}
balance, err := UnmarshalBalanceCompat(k.cdc, value, denom)
if err != nil {
return err
Expand Down
36 changes: 33 additions & 3 deletions x/bank/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,23 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
Limit: 1,
CountTotal: false,
}
req := types.NewQueryAllBalancesRequest(addr, pageReq)
req := types.NewQueryAllBalancesRequest(addr, pageReq, false)
res, err := queryClient.AllBalances(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.True(res.Balances.IsZero())

fooCoins := newFooCoin(50)
barCoins := newBarCoin(30)
ibcCoins := newIbcCoin(20)

origCoins := sdk.NewCoins(fooCoins, barCoins)
origCoins := sdk.NewCoins(fooCoins, barCoins, ibcCoins)

suite.mockFundAccount(addr)
suite.Require().NoError(testutil.FundAccount(suite.bankKeeper, ctx, addr, origCoins))

addIBCMetadata(ctx, suite.bankKeeper)

res, err = queryClient.AllBalances(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
Expand All @@ -83,10 +86,37 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
Limit: 1,
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addr, pageReq)
req = types.NewQueryAllBalancesRequest(addr, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Equal(res.Balances.Len(), 1)
suite.NotNil(res.Pagination.NextKey)

pageThree := res.Pagination.NextKey

suite.T().Log("query third page with nextkey")
pageReq = &query.PageRequest{
Key: pageThree,
Limit: 1,
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addr, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Equal(res.Balances.Len(), 1)
suite.Equal(res.Balances[0].Denom, ibcCoins.Denom)

suite.T().Log("query third page with nextkey and resolve ibc denom")
pageReq = &query.PageRequest{
Key: pageThree,
Limit: 1,
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addr, pageReq, true)
res, err = queryClient.AllBalances(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Equal(res.Balances.Len(), 1)
suite.Equal(res.Balances[0].Denom, ibcPath+"/"+ibcBaseDenom)
suite.Nil(res.Pagination.NextKey)
}

Expand Down
Loading

0 comments on commit ffde2b9

Please sign in to comment.