Add AllDenomMetadata BankQuery (#1426)

* x/wasm: add AllDenomMetadata BankQuery

* x/wasm: fix AllDenomMetadata BankQuery to have pagination and add DenomMetadata BankQuery

* Use simplified pagination

* Fix request conversion

* Add unknown denom test cases

* Add test for pagination conversion

* Fix nits

* Use wasmvm 1.3.0-rc.0

* Fix test

---------

Co-authored-by: Nikhil Suri <nikhilsuri@comcast.net>
This commit is contained in:
Christoph Otter
2023-07-06 14:57:05 +02:00
committed by GitHub
parent 17e14a4b1e
commit d2e9aceaaf
4 changed files with 220 additions and 5 deletions

View File

@@ -10,5 +10,6 @@ func AllCapabilities() []string {
"stargate",
"cosmwasm_1_1",
"cosmwasm_1_2",
"cosmwasm_1_3",
}
}

View File

@@ -6,17 +6,18 @@ import (
"fmt"
errorsmod "cosmossdk.io/errors"
"github.com/CosmWasm/wasmd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
type QueryHandler struct {
@@ -201,6 +202,27 @@ func BankQuerier(bankKeeper types.BankViewKeeper) func(ctx sdk.Context, request
}
return json.Marshal(res)
}
if request.DenomMetadata != nil {
denomMetadata, ok := bankKeeper.GetDenomMetaData(ctx, request.DenomMetadata.Denom)
if !ok {
return nil, errorsmod.Wrap(sdkerrors.ErrNotFound, request.DenomMetadata.Denom)
}
res := wasmvmtypes.DenomMetadataResponse{
Metadata: ConvertSdkDenomMetadataToWasmDenomMetadata(denomMetadata),
}
return json.Marshal(res)
}
if request.AllDenomMetadata != nil {
bankQueryRes, err := bankKeeper.DenomsMetadata(ctx, ConvertToDenomsMetadataRequest(request.AllDenomMetadata))
if err != nil {
return nil, sdkerrors.ErrInvalidRequest
}
res := wasmvmtypes.AllDenomMetadataResponse{
Metadata: ConvertSdkDenomMetadatasToWasmDenomMetadatas(bankQueryRes.Metadatas),
NextKey: bankQueryRes.Pagination.NextKey,
}
return json.Marshal(res)
}
return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown BankQuery variant"}
}
}
@@ -584,6 +606,51 @@ func ConvertSdkCoinToWasmCoin(coin sdk.Coin) wasmvmtypes.Coin {
}
}
func ConvertToDenomsMetadataRequest(wasmRequest *wasmvmtypes.AllDenomMetadataQuery) *banktypes.QueryDenomsMetadataRequest {
ret := &banktypes.QueryDenomsMetadataRequest{}
if wasmRequest.Pagination != nil {
ret.Pagination = &query.PageRequest{
Key: wasmRequest.Pagination.Key,
Limit: uint64(wasmRequest.Pagination.Limit),
Reverse: wasmRequest.Pagination.Reverse,
}
}
return ret
}
func ConvertSdkDenomMetadatasToWasmDenomMetadatas(metadata []banktypes.Metadata) []wasmvmtypes.DenomMetadata {
converted := make([]wasmvmtypes.DenomMetadata, len(metadata))
for i, m := range metadata {
converted[i] = ConvertSdkDenomMetadataToWasmDenomMetadata(m)
}
return converted
}
func ConvertSdkDenomMetadataToWasmDenomMetadata(metadata banktypes.Metadata) wasmvmtypes.DenomMetadata {
return wasmvmtypes.DenomMetadata{
Description: metadata.Description,
DenomUnits: ConvertSdkDenomUnitsToWasmDenomUnits(metadata.DenomUnits),
Base: metadata.Base,
Display: metadata.Display,
Name: metadata.Name,
Symbol: metadata.Symbol,
URI: metadata.URI,
URIHash: metadata.URIHash,
}
}
func ConvertSdkDenomUnitsToWasmDenomUnits(denomUnits []*banktypes.DenomUnit) []wasmvmtypes.DenomUnit {
converted := make([]wasmvmtypes.DenomUnit, len(denomUnits))
for i, u := range denomUnits {
converted[i] = wasmvmtypes.DenomUnit{
Denom: u.Denom,
Exponent: u.Exponent,
Aliases: u.Aliases,
}
}
return converted
}
// ConvertProtoToJSONMarshal unmarshals the given bytes into a proto message and then marshals it to json.
// This is done so that clients calling stargate queries do not need to define their own proto unmarshalers,
// being able to use response directly by json marshalling, which is supported in cosmwasm.

View File

@@ -1,6 +1,7 @@
package keeper_test
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
@@ -356,6 +357,133 @@ func TestBankQuerierBalance(t *testing.T) {
assert.Equal(t, exp, got)
}
func TestBankQuerierMetadata(t *testing.T) {
metadata := banktypes.Metadata{
Name: "Test Token",
Base: "utest",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
}
mock := bankKeeperMock{GetDenomMetadataFn: func(ctx sdk.Context, denom string) (banktypes.Metadata, bool) {
if denom == "utest" {
return metadata, true
} else {
return banktypes.Metadata{}, false
}
}}
ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
DenomMetadata: &wasmvmtypes.DenomMetadataQuery{
Denom: "utest",
},
})
require.NoError(t, gotErr)
var got wasmvmtypes.DenomMetadataResponse
require.NoError(t, json.Unmarshal(gotBz, &got))
exp := wasmvmtypes.DenomMetadata{
Name: "Test Token",
Base: "utest",
DenomUnits: []wasmvmtypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
}
assert.Equal(t, exp, got.Metadata)
_, gotErr2 := q(ctx, &wasmvmtypes.BankQuery{
DenomMetadata: &wasmvmtypes.DenomMetadataQuery{
Denom: "uatom",
},
})
require.Error(t, gotErr2)
assert.Contains(t, gotErr2.Error(), "uatom: not found")
}
func TestBankQuerierAllMetadata(t *testing.T) {
metadata := []banktypes.Metadata{
{
Name: "Test Token",
Base: "utest",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
},
}
mock := bankKeeperMock{GetDenomsMetadataFn: func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
return &banktypes.QueryDenomsMetadataResponse{
Metadatas: metadata,
Pagination: &query.PageResponse{},
}, nil
}}
ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
AllDenomMetadata: &wasmvmtypes.AllDenomMetadataQuery{},
})
require.NoError(t, gotErr)
var got wasmvmtypes.AllDenomMetadataResponse
require.NoError(t, json.Unmarshal(gotBz, &got))
exp := wasmvmtypes.AllDenomMetadataResponse{
Metadata: []wasmvmtypes.DenomMetadata{
{
Name: "Test Token",
Base: "utest",
DenomUnits: []wasmvmtypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
},
},
}
assert.Equal(t, exp, got)
}
func TestBankQuerierAllMetadataPagination(t *testing.T) {
var capturedPagination *query.PageRequest
mock := bankKeeperMock{GetDenomsMetadataFn: func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
capturedPagination = req.Pagination
return &banktypes.QueryDenomsMetadataResponse{
Metadatas: []banktypes.Metadata{},
Pagination: &query.PageResponse{
NextKey: nil,
},
}, nil
}}
ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
_, gotErr := q(ctx, &wasmvmtypes.BankQuery{
AllDenomMetadata: &wasmvmtypes.AllDenomMetadataQuery{
Pagination: &wasmvmtypes.PageRequest{
Key: []byte("key"),
Limit: 10,
},
},
})
require.NoError(t, gotErr)
exp := &query.PageRequest{
Key: []byte("key"),
Limit: 10,
}
assert.Equal(t, exp, capturedPagination)
}
func TestContractInfoWasmQuerier(t *testing.T) {
myValidContractAddr := keeper.RandomBech32AccountAddress(t)
myCreatorAddr := keeper.RandomBech32AccountAddress(t)
@@ -676,6 +804,8 @@ type bankKeeperMock struct {
GetSupplyFn func(ctx sdk.Context, denom string) sdk.Coin
GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetDenomMetadataFn func(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
GetDenomsMetadataFn func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error)
}
func (m bankKeeperMock) GetSupply(ctx sdk.Context, denom string) sdk.Coin {
@@ -699,6 +829,20 @@ func (m bankKeeperMock) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk
return m.GetAllBalancesFn(ctx, addr)
}
func (m bankKeeperMock) GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool) {
if m.GetDenomMetadataFn == nil {
panic("not expected to be called")
}
return m.GetDenomMetadataFn(ctx, denom)
}
func (m bankKeeperMock) DenomsMetadata(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
if m.GetDenomsMetadataFn == nil {
panic("not expected to be called")
}
return m.GetDenomsMetadataFn(ctx, req)
}
func TestConvertProtoToJSONMarshal(t *testing.T) {
testCases := []struct {
name string

View File

@@ -7,6 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
@@ -20,6 +21,8 @@ type BankViewKeeper interface {
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetSupply(ctx sdk.Context, denom string) sdk.Coin
GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
DenomsMetadata(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error)
}
// Burner is a subset of the sdk bank keeper methods