wasmd/x/wasm/keeper/query_plugins_test.go

529 lines
16 KiB
Go

package keeper
import (
"encoding/json"
"testing"
"github.com/cosmos/cosmos-sdk/store"
dbm "github.com/tendermint/tm-db"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
channeltypes "github.com/cosmos/ibc-go/v2/modules/core/04-channel/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
func TestIBCQuerier(t *testing.T) {
myExampleChannels := []channeltypes.IdentifiedChannel{
// this is returned
{
State: channeltypes.OPEN,
Ordering: channeltypes.ORDERED,
Counterparty: channeltypes.Counterparty{
PortId: "counterPartyPortID",
ChannelId: "counterPartyChannelID",
},
ConnectionHops: []string{"one"},
Version: "v1",
PortId: "myPortID",
ChannelId: "myChannelID",
},
// this is filtered out
{
State: channeltypes.INIT,
Ordering: channeltypes.UNORDERED,
Counterparty: channeltypes.Counterparty{
PortId: "foobar",
},
ConnectionHops: []string{"one"},
Version: "initversion",
PortId: "initPortID",
ChannelId: "initChannelID",
},
// this is returned
{
State: channeltypes.OPEN,
Ordering: channeltypes.UNORDERED,
Counterparty: channeltypes.Counterparty{
PortId: "otherCounterPartyPortID",
ChannelId: "otherCounterPartyChannelID",
},
ConnectionHops: []string{"other", "second"},
Version: "otherVersion",
PortId: "otherPortID",
ChannelId: "otherChannelID",
},
// this is filtered out
{
State: channeltypes.CLOSED,
Ordering: channeltypes.ORDERED,
Counterparty: channeltypes.Counterparty{
PortId: "super",
ChannelId: "duper",
},
ConnectionHops: []string{"no-more"},
Version: "closedVersion",
PortId: "closedPortID",
ChannelId: "closedChannelID",
},
}
specs := map[string]struct {
srcQuery *wasmvmtypes.IBCQuery
wasmKeeper *mockWasmQueryKeeper
channelKeeper *wasmtesting.MockChannelKeeper
expJsonResult string
expErr *sdkerrors.Error
}{
"query port id": {
srcQuery: &wasmvmtypes.IBCQuery{
PortID: &wasmvmtypes.PortIDQuery{},
},
wasmKeeper: &mockWasmQueryKeeper{
GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
return &types.ContractInfo{IBCPortID: "myIBCPortID"}
},
},
channelKeeper: &wasmtesting.MockChannelKeeper{},
expJsonResult: `{"port_id":"myIBCPortID"}`,
},
"query list channels - all": {
srcQuery: &wasmvmtypes.IBCQuery{
ListChannels: &wasmvmtypes.ListChannelsQuery{},
},
channelKeeper: &wasmtesting.MockChannelKeeper{
IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
},
expJsonResult: `{
"channels": [
{
"endpoint": {
"port_id": "myPortID",
"channel_id": "myChannelID"
},
"counterparty_endpoint": {
"port_id": "counterPartyPortID",
"channel_id": "counterPartyChannelID"
},
"order": "ORDER_ORDERED",
"version": "v1",
"connection_id": "one"
},
{
"endpoint": {
"port_id": "otherPortID",
"channel_id": "otherChannelID"
},
"counterparty_endpoint": {
"port_id": "otherCounterPartyPortID",
"channel_id": "otherCounterPartyChannelID"
},
"order": "ORDER_UNORDERED",
"version": "otherVersion",
"connection_id": "other"
}
]
}`,
},
"query list channels - filtered": {
srcQuery: &wasmvmtypes.IBCQuery{
ListChannels: &wasmvmtypes.ListChannelsQuery{
PortID: "otherPortID",
},
},
channelKeeper: &wasmtesting.MockChannelKeeper{
IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
},
expJsonResult: `{
"channels": [
{
"endpoint": {
"port_id": "otherPortID",
"channel_id": "otherChannelID"
},
"counterparty_endpoint": {
"port_id": "otherCounterPartyPortID",
"channel_id": "otherCounterPartyChannelID"
},
"order": "ORDER_UNORDERED",
"version": "otherVersion",
"connection_id": "other"
}
]
}`,
},
"query list channels - filtered empty": {
srcQuery: &wasmvmtypes.IBCQuery{
ListChannels: &wasmvmtypes.ListChannelsQuery{
PortID: "none-existing",
},
},
channelKeeper: &wasmtesting.MockChannelKeeper{
IterateChannelsFn: wasmtesting.MockChannelKeeperIterator(myExampleChannels),
},
expJsonResult: `{"channels": []}`,
},
"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 sdk.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 := IBCQuerier(spec.wasmKeeper, spec.channelKeeper)
gotResult, gotErr := h(sdk.Context{}, 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 sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
return sdk.NewCoin(denom, sdk.NewInt(1))
}}
ctx := sdk.Context{}
q := BankQuerier(mock)
gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
Balance: &wasmvmtypes.BalanceQuery{
Address: 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 TestContractInfoWasmQuerier(t *testing.T) {
var myValidContractAddr = RandomBech32AccountAddress(t)
var myCreatorAddr = RandomBech32AccountAddress(t)
var myAdminAddr = 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 sdk.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 sdk.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 sdk.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 sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
val := types.ContractInfoFixture(func(i *types.ContractInfo) {
i.Admin, i.Creator = myAdminAddr, myCreatorAddr
})
return &val
},
IsPinnedCodeFn: func(ctx sdk.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 sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
val := types.ContractInfoFixture(func(i *types.ContractInfo) {
i.Creator = myCreatorAddr
})
return &val
},
IsPinnedCodeFn: func(ctx sdk.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 := 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 TestQueryErrors(t *testing.T) {
specs := map[string]struct {
src error
expErr error
}{
"no error": {},
"no such contract": {
src: &types.ErrNoSuchContract{Addr: "contract-addr"},
expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
},
"no such contract - wrapped": {
src: sdkerrors.Wrap(&types.ErrNoSuchContract{Addr: "contract-addr"}, "my additional data"),
expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
mock := WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) {
return nil, spec.src
})
ctx := sdk.Context{}.WithGasMeter(sdk.NewInfiniteGasMeter()).WithMultiStore(store.NewCommitMultiStore(dbm.NewMemDB()))
q := NewQueryHandler(ctx, mock, sdk.AccAddress{}, NewDefaultWasmGasRegister())
_, gotErr := q.Query(wasmvmtypes.QueryRequest{}, 1)
assert.Equal(t, spec.expErr, gotErr)
})
}
}
type mockWasmQueryKeeper struct {
GetContractInfoFn func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo
QueryRawFn func(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
QuerySmartFn func(ctx sdk.Context, contractAddr sdk.AccAddress, req types.RawContractMessage) ([]byte, error)
IsPinnedCodeFn func(ctx sdk.Context, codeID uint64) bool
}
func (m mockWasmQueryKeeper) GetContractInfo(ctx sdk.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 sdk.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 sdk.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 sdk.Context, codeID uint64) bool {
if m.IsPinnedCodeFn == nil {
panic("not expected to be called")
}
return m.IsPinnedCodeFn(ctx, codeID)
}
type bankKeeperMock struct {
GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
}
func (m bankKeeperMock) GetBalance(ctx sdk.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 sdk.Context, addr sdk.AccAddress) sdk.Coins {
if m.GetAllBalancesFn == nil {
panic("not expected to be called")
}
return m.GetAllBalancesFn(ctx, addr)
}