package keeper_test import ( "context" "encoding/json" "fmt" "math" "sync/atomic" "testing" wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types" abci "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/gogoproto/proto" channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" sdkmath "cosmossdk.io/math" "cosmossdk.io/store" storemetrics "cosmossdk.io/store/metrics" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" "github.com/CosmWasm/wasmd/x/wasm/types" ) func TestIBCQuerier(t *testing.T) { specs := map[string]struct { srcQuery *wasmvmtypes.IBCQuery wasmKeeper *mockWasmQueryKeeper channelKeeper *wasmtesting.MockChannelKeeper expJSONResult string expErr *errorsmod.Error }{ "query port id": { srcQuery: &wasmvmtypes.IBCQuery{ PortID: &wasmvmtypes.PortIDQuery{}, }, wasmKeeper: &mockWasmQueryKeeper{ GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { return &types.ContractInfo{IBCPortID: "myIBCPortID"} }, }, channelKeeper: &wasmtesting.MockChannelKeeper{}, expJSONResult: `{"port_id":"myIBCPortID"}`, }, "query channel": { srcQuery: &wasmvmtypes.IBCQuery{ Channel: &wasmvmtypes.ChannelQuery{ PortID: "myQueryPortID", ChannelID: "myQueryChannelID", }, }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{ State: channeltypes.OPEN, Ordering: channeltypes.UNORDERED, Counterparty: channeltypes.Counterparty{ PortId: "counterPartyPortID", ChannelId: "otherCounterPartyChannelID", }, ConnectionHops: []string{"one"}, Version: "version", }, true }, }, expJSONResult: `{ "channel": { "endpoint": { "port_id": "myQueryPortID", "channel_id": "myQueryChannelID" }, "counterparty_endpoint": { "port_id": "counterPartyPortID", "channel_id": "otherCounterPartyChannelID" }, "order": "ORDER_UNORDERED", "version": "version", "connection_id": "one" } }`, }, "query channel - without port set": { srcQuery: &wasmvmtypes.IBCQuery{ Channel: &wasmvmtypes.ChannelQuery{ ChannelID: "myQueryChannelID", }, }, wasmKeeper: &mockWasmQueryKeeper{ GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { return &types.ContractInfo{IBCPortID: "myLoadedPortID"} }, }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{ State: channeltypes.OPEN, Ordering: channeltypes.UNORDERED, Counterparty: channeltypes.Counterparty{ PortId: "counterPartyPortID", ChannelId: "otherCounterPartyChannelID", }, ConnectionHops: []string{"one"}, Version: "version", }, true }, }, expJSONResult: `{ "channel": { "endpoint": { "port_id": "myLoadedPortID", "channel_id": "myQueryChannelID" }, "counterparty_endpoint": { "port_id": "counterPartyPortID", "channel_id": "otherCounterPartyChannelID" }, "order": "ORDER_UNORDERED", "version": "version", "connection_id": "one" } }`, }, "query channel in init state": { srcQuery: &wasmvmtypes.IBCQuery{ Channel: &wasmvmtypes.ChannelQuery{ PortID: "myQueryPortID", ChannelID: "myQueryChannelID", }, }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{ State: channeltypes.INIT, Ordering: channeltypes.UNORDERED, Counterparty: channeltypes.Counterparty{ PortId: "foobar", }, ConnectionHops: []string{"one"}, Version: "initversion", }, true }, }, expJSONResult: "{}", }, "query channel in closed state": { srcQuery: &wasmvmtypes.IBCQuery{ Channel: &wasmvmtypes.ChannelQuery{ PortID: "myQueryPortID", ChannelID: "myQueryChannelID", }, }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{ State: channeltypes.CLOSED, Ordering: channeltypes.ORDERED, Counterparty: channeltypes.Counterparty{ PortId: "super", ChannelId: "duper", }, ConnectionHops: []string{"no-more"}, Version: "closedVersion", }, true }, }, expJSONResult: "{}", }, "query channel - empty result": { srcQuery: &wasmvmtypes.IBCQuery{ Channel: &wasmvmtypes.ChannelQuery{ PortID: "myQueryPortID", ChannelID: "myQueryChannelID", }, }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{}, false }, }, expJSONResult: "{}", }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { h := keeper.IBCQuerier(spec.wasmKeeper, spec.channelKeeper) gotResult, gotErr := h(sdk.Context{}, keeper.RandomAccountAddress(t), spec.srcQuery) require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) if spec.expErr != nil { return } assert.JSONEq(t, spec.expJSONResult, string(gotResult), string(gotResult)) }) } } func TestBankQuerierBalance(t *testing.T) { mock := bankKeeperMock{GetBalanceFn: func(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin { return sdk.NewCoin(denom, sdkmath.NewInt(1)) }} ctx := sdk.Context{} q := keeper.BankQuerier(mock) gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{ Balance: &wasmvmtypes.BalanceQuery{ Address: keeper.RandomBech32AccountAddress(t), Denom: "ALX", }, }) require.NoError(t, gotErr) var got wasmvmtypes.BalanceResponse require.NoError(t, json.Unmarshal(gotBz, &got)) exp := wasmvmtypes.BalanceResponse{ Amount: wasmvmtypes.Coin{ Denom: "ALX", Amount: "1", }, } 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, Aliases: []string{}, }, }, } mock := bankKeeperMock{GetDenomMetadataFn: func(ctx context.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, Aliases: []string{}, }, }, } 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 TestBankQuerierMetadataWithNilAliases(t *testing.T) { metadata := banktypes.Metadata{ Name: "Test Token", Base: "utest", DenomUnits: []*banktypes.DenomUnit{ { Denom: "utest", Exponent: 0, Aliases: nil, }, }, } mock := bankKeeperMock{GetDenomMetadataFn: func(ctx context.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, Aliases: []string{}, }, }, } 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, Aliases: []string{}, }, }, }, } 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, Aliases: []string{}, }, }, }, }, } 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) myAdminAddr := keeper.RandomBech32AccountAddress(t) var ctx sdk.Context specs := map[string]struct { req *wasmvmtypes.WasmQuery mock mockWasmQueryKeeper expRes wasmvmtypes.ContractInfoResponse expErr bool }{ "all good": { req: &wasmvmtypes.WasmQuery{ ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, }, mock: mockWasmQueryKeeper{ GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { val := types.ContractInfoFixture(func(i *types.ContractInfo) { i.Admin, i.Creator, i.IBCPortID = myAdminAddr, myCreatorAddr, "myIBCPort" }) return &val }, IsPinnedCodeFn: func(ctx context.Context, codeID uint64) bool { return true }, }, expRes: wasmvmtypes.ContractInfoResponse{ CodeID: 1, Creator: myCreatorAddr, Admin: myAdminAddr, Pinned: true, IBCPort: "myIBCPort", }, }, "invalid addr": { req: &wasmvmtypes.WasmQuery{ ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: "not a valid addr"}, }, expErr: true, }, "unknown addr": { req: &wasmvmtypes.WasmQuery{ ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, }, mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { return nil }}, expErr: true, }, "not pinned": { req: &wasmvmtypes.WasmQuery{ ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, }, mock: mockWasmQueryKeeper{ GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { val := types.ContractInfoFixture(func(i *types.ContractInfo) { i.Admin, i.Creator = myAdminAddr, myCreatorAddr }) return &val }, IsPinnedCodeFn: func(ctx context.Context, codeID uint64) bool { return false }, }, expRes: wasmvmtypes.ContractInfoResponse{ CodeID: 1, Creator: myCreatorAddr, Admin: myAdminAddr, Pinned: false, }, }, "without admin": { req: &wasmvmtypes.WasmQuery{ ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, }, mock: mockWasmQueryKeeper{ GetContractInfoFn: func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { val := types.ContractInfoFixture(func(i *types.ContractInfo) { i.Creator = myCreatorAddr }) return &val }, IsPinnedCodeFn: func(ctx context.Context, codeID uint64) bool { return true }, }, expRes: wasmvmtypes.ContractInfoResponse{ CodeID: 1, Creator: myCreatorAddr, Pinned: true, }, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { q := keeper.WasmQuerier(spec.mock) gotBz, gotErr := q(ctx, spec.req) if spec.expErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) var gotRes wasmvmtypes.ContractInfoResponse require.NoError(t, json.Unmarshal(gotBz, &gotRes)) assert.Equal(t, spec.expRes, gotRes) }) } } func TestCodeInfoWasmQuerier(t *testing.T) { myCreatorAddr := keeper.RandomBech32AccountAddress(t) var ctx sdk.Context myRawChecksum := []byte("myHash78901234567890123456789012") specs := map[string]struct { req *wasmvmtypes.WasmQuery mock mockWasmQueryKeeper expRes wasmvmtypes.CodeInfoResponse expErr bool }{ "all good": { req: &wasmvmtypes.WasmQuery{ CodeInfo: &wasmvmtypes.CodeInfoQuery{CodeID: 1}, }, mock: mockWasmQueryKeeper{ GetCodeInfoFn: func(ctx context.Context, codeID uint64) *types.CodeInfo { return &types.CodeInfo{ CodeHash: myRawChecksum, Creator: myCreatorAddr, InstantiateConfig: types.AccessConfig{ Permission: types.AccessTypeNobody, Addresses: []string{myCreatorAddr}, }, } }, }, expRes: wasmvmtypes.CodeInfoResponse{ CodeID: 1, Creator: myCreatorAddr, Checksum: myRawChecksum, }, }, "empty code id": { req: &wasmvmtypes.WasmQuery{ CodeInfo: &wasmvmtypes.CodeInfoQuery{}, }, expErr: true, }, "unknown code id": { req: &wasmvmtypes.WasmQuery{ CodeInfo: &wasmvmtypes.CodeInfoQuery{CodeID: 1}, }, mock: mockWasmQueryKeeper{ GetCodeInfoFn: func(ctx context.Context, codeID uint64) *types.CodeInfo { return nil }, }, expErr: true, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { q := keeper.WasmQuerier(spec.mock) gotBz, gotErr := q(ctx, spec.req) if spec.expErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) var gotRes wasmvmtypes.CodeInfoResponse require.NoError(t, json.Unmarshal(gotBz, &gotRes), string(gotBz)) assert.Equal(t, spec.expRes, gotRes) }) } } func TestQueryErrors(t *testing.T) { specs := map[string]struct { src error expErr error }{ "no error": {}, "no such contract": { src: types.ErrNoSuchContractFn("contract-addr"), expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, }, "no such contract - wrapped": { src: errorsmod.Wrap(types.ErrNoSuchContractFn("contract-addr"), "my additional data"), expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, }, "no such code": { src: types.ErrNoSuchCodeFn(123), expErr: wasmvmtypes.NoSuchCode{CodeID: 123}, }, "no such code - wrapped": { src: errorsmod.Wrap(types.ErrNoSuchCodeFn(123), "my additional data"), expErr: wasmvmtypes.NoSuchCode{CodeID: 123}, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { mock := keeper.WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { return nil, spec.src }) ms := store.NewCommitMultiStore(dbm.NewMemDB(), log.NewTestLogger(t), storemetrics.NewNoOpMetrics()) ctx := sdk.NewContext(ms, cmtproto.Header{}, false, log.NewTestLogger(t)).WithGasMeter(storetypes.NewInfiniteGasMeter()) q := keeper.NewQueryHandler(ctx, mock, sdk.AccAddress{}, types.NewDefaultWasmGasRegister()) _, gotErr := q.Query(wasmvmtypes.QueryRequest{}, 1) assert.Equal(t, spec.expErr, gotErr) }) } } type mockWasmQueryKeeper struct { GetContractInfoFn func(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo QueryRawFn func(ctx context.Context, contractAddress sdk.AccAddress, key []byte) []byte QuerySmartFn func(ctx context.Context, contractAddr sdk.AccAddress, req types.RawContractMessage) ([]byte, error) IsPinnedCodeFn func(ctx context.Context, codeID uint64) bool GetCodeInfoFn func(ctx context.Context, codeID uint64) *types.CodeInfo } func (m mockWasmQueryKeeper) GetContractInfo(ctx context.Context, contractAddress sdk.AccAddress) *types.ContractInfo { if m.GetContractInfoFn == nil { panic("not expected to be called") } return m.GetContractInfoFn(ctx, contractAddress) } func (m mockWasmQueryKeeper) QueryRaw(ctx context.Context, contractAddress sdk.AccAddress, key []byte) []byte { if m.QueryRawFn == nil { panic("not expected to be called") } return m.QueryRawFn(ctx, contractAddress, key) } func (m mockWasmQueryKeeper) QuerySmart(ctx context.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { if m.QuerySmartFn == nil { panic("not expected to be called") } return m.QuerySmartFn(ctx, contractAddr, req) } func (m mockWasmQueryKeeper) IsPinnedCode(ctx context.Context, codeID uint64) bool { if m.IsPinnedCodeFn == nil { panic("not expected to be called") } return m.IsPinnedCodeFn(ctx, codeID) } func (m mockWasmQueryKeeper) GetCodeInfo(ctx context.Context, codeID uint64) *types.CodeInfo { if m.GetCodeInfoFn == nil { panic("not expected to be called") } return m.GetCodeInfoFn(ctx, codeID) } type bankKeeperMock struct { GetSupplyFn func(ctx context.Context, denom string) sdk.Coin GetBalanceFn func(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin GetAllBalancesFn func(ctx context.Context, addr sdk.AccAddress) sdk.Coins GetDenomMetadataFn func(ctx context.Context, denom string) (banktypes.Metadata, bool) GetDenomsMetadataFn func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) } func (m bankKeeperMock) GetSupply(ctx context.Context, denom string) sdk.Coin { if m.GetSupplyFn == nil { panic("not expected to be called") } return m.GetSupplyFn(ctx, denom) } func (m bankKeeperMock) GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin { if m.GetBalanceFn == nil { panic("not expected to be called") } return m.GetBalanceFn(ctx, addr, denom) } func (m bankKeeperMock) GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins { if m.GetAllBalancesFn == nil { panic("not expected to be called") } return m.GetAllBalancesFn(ctx, addr) } func (m bankKeeperMock) GetDenomMetaData(ctx context.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 TestConvertSDKDecCoinToWasmDecCoin(t *testing.T) { specs := map[string]struct { src sdk.DecCoins exp []wasmvmtypes.DecCoin }{ "one coin": { src: sdk.NewDecCoins(sdk.NewInt64DecCoin("alx", 1)), exp: []wasmvmtypes.DecCoin{{Amount: "1.000000000000000000", Denom: "alx"}}, }, "multiple coins": { src: sdk.NewDecCoins(sdk.NewInt64DecCoin("alx", 1), sdk.NewInt64DecCoin("blx", 2)), exp: []wasmvmtypes.DecCoin{{Amount: "1.000000000000000000", Denom: "alx"}, {Amount: "2.000000000000000000", Denom: "blx"}}, }, "small amount": { src: sdk.NewDecCoins(sdk.NewDecCoinFromDec("alx", sdkmath.LegacyNewDecWithPrec(1, 18))), exp: []wasmvmtypes.DecCoin{{Amount: "0.000000000000000001", Denom: "alx"}}, }, "big amount": { src: sdk.NewDecCoins(sdk.NewDecCoin("alx", sdkmath.NewIntFromUint64(math.MaxUint64))), exp: []wasmvmtypes.DecCoin{{Amount: "18446744073709551615.000000000000000000", Denom: "alx"}}, }, "empty": { src: sdk.NewDecCoins(), exp: []wasmvmtypes.DecCoin{}, }, "nil": { exp: []wasmvmtypes.DecCoin{}, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { got := keeper.ConvertSDKDecCoinsToWasmDecCoins(spec.src) assert.Equal(t, spec.exp, got) }) } } var _ keeper.GRPCQueryRouter = mockedQueryRouter{} type mockedQueryRouter struct { codec codec.Codec } func (m mockedQueryRouter) Route(_ string) baseapp.GRPCQueryHandler { return func(ctx sdk.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) { balanceReq := &banktypes.QueryBalanceRequest{} if err := m.codec.Unmarshal(req.Data, balanceReq); err != nil { return nil, err } coin := sdk.NewInt64Coin(balanceReq.Denom, 1) balanceRes := &banktypes.QueryBalanceResponse{ Balance: &coin, } resValue, err := m.codec.Marshal(balanceRes) if err != nil { return nil, err } return &abci.ResponseQuery{ Value: resValue, }, nil } } func TestGRPCQuerier(t *testing.T) { const ( denom1 = "denom1" denom2 = "denom2" ) _, keepers := keeper.CreateTestInput(t, false, keeper.AvailableCapabilities) cdc := keepers.EncodingConfig.Codec acceptedQueries := keeper.AcceptedQueries{ "/bank.Balance": func() proto.Message { return &banktypes.QueryBalanceResponse{} }, } router := mockedQueryRouter{ codec: cdc, } querier := keeper.AcceptListStargateQuerier(acceptedQueries, router, keepers.EncodingConfig.Codec) addr := keeper.RandomBech32AccountAddress(t) eg := errgroup.Group{} errorsCount := atomic.Uint64{} for range 50 { for _, denom := range []string{denom1, denom2} { denom := denom // copy eg.Go(func() error { queryReq := &banktypes.QueryBalanceRequest{ Address: addr, Denom: denom, } grpcData, err := cdc.Marshal(queryReq) if err != nil { return err } wasmGrpcReq := &wasmvmtypes.StargateQuery{ Data: grpcData, Path: "/bank.Balance", } wasmGrpcRes, err := querier(sdk.Context{}, wasmGrpcReq) if err != nil { return err } queryRes := &banktypes.QueryBalanceResponse{} if err := cdc.UnmarshalJSON(wasmGrpcRes, queryRes); err != nil { return err } expectedCoin := sdk.NewInt64Coin(denom, 1) if queryRes.Balance == nil { fmt.Printf( "Error: expected %s, got nil\n", expectedCoin.String(), ) errorsCount.Add(1) return nil } if queryRes.Balance.String() != expectedCoin.String() { fmt.Printf( "Error: expected %s, got %s\n", expectedCoin.String(), queryRes.Balance.String(), ) errorsCount.Add(1) return nil } return nil }) } } require.NoError(t, eg.Wait()) require.Zero(t, errorsCount.Load()) }