forked from cerc-io/laconicd-deprecated
826 lines
26 KiB
Go
826 lines
26 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
|
"github.com/cosmos/cosmos-sdk/store"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/types/query"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
dbm "github.com/tendermint/tm-db"
|
|
|
|
"github.com/cerc-io/laconicd/app"
|
|
"github.com/cerc-io/laconicd/x/wasm/keeper"
|
|
"github.com/cerc-io/laconicd/x/wasm/keeper/wasmtesting"
|
|
"github.com/cerc-io/laconicd/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 := 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 sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
|
|
return sdk.NewCoin(denom, sdk.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 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 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 := 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 sdk.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 sdk.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: sdkerrors.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: sdkerrors.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
|
|
})
|
|
ctx := sdk.Context{}.WithGasMeter(sdk.NewInfiniteGasMeter()).WithMultiStore(store.NewCommitMultiStore(dbm.NewMemDB()))
|
|
q := keeper.NewQueryHandler(ctx, mock, sdk.AccAddress{}, keeper.NewDefaultWasmGasRegister())
|
|
_, gotErr := q.Query(wasmvmtypes.QueryRequest{}, 1)
|
|
assert.Equal(t, spec.expErr, gotErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAcceptListStargateQuerier(t *testing.T) {
|
|
wasmApp := app.SetupWithEmptyStore(t)
|
|
ctx := wasmApp.NewUncachedContext(false, tmproto.Header{ChainID: "foo", Height: 1, Time: time.Now()})
|
|
wasmApp.StakingKeeper.SetParams(ctx, stakingtypes.DefaultParams())
|
|
|
|
addrs := app.AddTestAddrs(wasmApp, ctx, 2, sdk.NewInt(1_000_000))
|
|
accepted := keeper.AcceptedStargateQueries{
|
|
"/cosmos.auth.v1beta1.Query/Account": &authtypes.QueryAccountResponse{},
|
|
"/no/route/to/this": &authtypes.QueryAccountResponse{},
|
|
}
|
|
|
|
marshal := func(pb proto.Message) []byte {
|
|
b, err := proto.Marshal(pb)
|
|
require.NoError(t, err)
|
|
return b
|
|
}
|
|
|
|
specs := map[string]struct {
|
|
req *wasmvmtypes.StargateQuery
|
|
expErr bool
|
|
expResp string
|
|
}{
|
|
"in accept list - success result": {
|
|
req: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.auth.v1beta1.Query/Account",
|
|
Data: marshal(&authtypes.QueryAccountRequest{Address: addrs[0].String()}),
|
|
},
|
|
expResp: fmt.Sprintf(`{"account":{"@type":"/cosmos.auth.v1beta1.BaseAccount","address":%q,"pub_key":null,"account_number":"1","sequence":"0"}}`, addrs[0].String()),
|
|
},
|
|
"in accept list - error result": {
|
|
req: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.auth.v1beta1.Query/Account",
|
|
Data: marshal(&authtypes.QueryAccountRequest{Address: sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()).String()}),
|
|
},
|
|
expErr: true,
|
|
},
|
|
"not in accept list": {
|
|
req: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.bank.v1beta1.Query/AllBalances",
|
|
Data: marshal(&banktypes.QueryAllBalancesRequest{Address: addrs[0].String()}),
|
|
},
|
|
expErr: true,
|
|
},
|
|
"unknown route": {
|
|
req: &wasmvmtypes.StargateQuery{
|
|
Path: "/no/route/to/this",
|
|
Data: marshal(&banktypes.QueryAllBalancesRequest{Address: addrs[0].String()}),
|
|
},
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
q := keeper.AcceptListStargateQuerier(accepted, wasmApp.GRPCQueryRouter(), wasmApp.AppCodec())
|
|
gotBz, gotErr := q(ctx, spec.req)
|
|
if spec.expErr {
|
|
require.Error(t, gotErr)
|
|
return
|
|
}
|
|
require.NoError(t, gotErr)
|
|
assert.JSONEq(t, spec.expResp, string(gotBz), string(gotBz))
|
|
})
|
|
}
|
|
}
|
|
|
|
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
|
|
GetCodeInfoFn func(ctx sdk.Context, codeID uint64) *types.CodeInfo
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
func (m mockWasmQueryKeeper) GetCodeInfo(ctx sdk.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 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
|
|
}
|
|
|
|
func (m bankKeeperMock) GetSupply(ctx sdk.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 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)
|
|
}
|
|
|
|
func TestConvertProtoToJSONMarshal(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
queryPath string
|
|
protoResponseStruct codec.ProtoMarshaler
|
|
originalResponse string
|
|
expectedProtoResponse codec.ProtoMarshaler
|
|
expectedError bool
|
|
}{
|
|
{
|
|
name: "successful conversion from proto response to json marshalled response",
|
|
queryPath: "/cosmos.bank.v1beta1.Query/AllBalances",
|
|
originalResponse: "0a090a036261721202333012050a03666f6f",
|
|
protoResponseStruct: &banktypes.QueryAllBalancesResponse{},
|
|
expectedProtoResponse: &banktypes.QueryAllBalancesResponse{
|
|
Balances: sdk.NewCoins(sdk.NewCoin("bar", sdk.NewInt(30))),
|
|
Pagination: &query.PageResponse{
|
|
NextKey: []byte("foo"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid proto response struct",
|
|
queryPath: "/cosmos.bank.v1beta1.Query/AllBalances",
|
|
originalResponse: "0a090a036261721202333012050a03666f6f",
|
|
protoResponseStruct: &authtypes.QueryAccountResponse{},
|
|
expectedError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) {
|
|
originalVersionBz, err := hex.DecodeString(tc.originalResponse)
|
|
require.NoError(t, err)
|
|
appCodec := app.MakeEncodingConfig().Marshaler
|
|
|
|
jsonMarshalledResponse, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.protoResponseStruct, originalVersionBz)
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// check response by json marshalling proto response into json response manually
|
|
jsonMarshalExpectedResponse, err := appCodec.MarshalJSON(tc.expectedProtoResponse)
|
|
require.NoError(t, err)
|
|
require.JSONEq(t, string(jsonMarshalledResponse), string(jsonMarshalExpectedResponse))
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDeterministicJsonMarshal tests that we get deterministic JSON marshalled response upon
|
|
// proto struct update in the state machine.
|
|
func TestDeterministicJsonMarshal(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
originalResponse string
|
|
updatedResponse string
|
|
queryPath string
|
|
responseProtoStruct codec.ProtoMarshaler
|
|
expectedProto func() codec.ProtoMarshaler
|
|
}{
|
|
/**
|
|
*
|
|
* Origin Response
|
|
* 0a530a202f636f736d6f732e617574682e763162657461312e426173654163636f756e74122f0a2d636f736d6f7331346c3268686a6e676c3939367772703935673867646a6871653038326375367a7732706c686b
|
|
*
|
|
* Updated Response
|
|
* 0a530a202f636f736d6f732e617574682e763162657461312e426173654163636f756e74122f0a2d636f736d6f7331646a783375676866736d6b6135386676673076616a6e6533766c72776b7a6a346e6377747271122d636f736d6f7331646a783375676866736d6b6135386676673076616a6e6533766c72776b7a6a346e6377747271
|
|
// Origin proto
|
|
message QueryAccountResponse {
|
|
// account defines the account of the corresponding address.
|
|
google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"];
|
|
}
|
|
// Updated proto
|
|
message QueryAccountResponse {
|
|
// account defines the account of the corresponding address.
|
|
google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"];
|
|
// address is the address to query for.
|
|
string address = 2;
|
|
}
|
|
*/
|
|
{
|
|
"Query Account",
|
|
"0a530a202f636f736d6f732e617574682e763162657461312e426173654163636f756e74122f0a2d636f736d6f733166387578756c746e3873717a687a6e72737a3371373778776171756867727367366a79766679",
|
|
"0a530a202f636f736d6f732e617574682e763162657461312e426173654163636f756e74122f0a2d636f736d6f733166387578756c746e3873717a687a6e72737a3371373778776171756867727367366a79766679122d636f736d6f733166387578756c746e3873717a687a6e72737a3371373778776171756867727367366a79766679",
|
|
"/cosmos.auth.v1beta1.Query/Account",
|
|
&authtypes.QueryAccountResponse{},
|
|
func() codec.ProtoMarshaler {
|
|
account := authtypes.BaseAccount{
|
|
Address: "cosmos1f8uxultn8sqzhznrsz3q77xwaquhgrsg6jyvfy",
|
|
}
|
|
accountResponse, err := codectypes.NewAnyWithValue(&account)
|
|
require.NoError(t, err)
|
|
return &authtypes.QueryAccountResponse{
|
|
Account: accountResponse,
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) {
|
|
appCodec := app.MakeEncodingConfig().Marshaler
|
|
|
|
originVersionBz, err := hex.DecodeString(tc.originalResponse)
|
|
require.NoError(t, err)
|
|
jsonMarshalledOriginalBz, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, originVersionBz)
|
|
require.NoError(t, err)
|
|
|
|
newVersionBz, err := hex.DecodeString(tc.updatedResponse)
|
|
require.NoError(t, err)
|
|
jsonMarshalledUpdatedBz, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, newVersionBz)
|
|
require.NoError(t, err)
|
|
|
|
// json marshalled bytes should be the same since we use the same proto struct for unmarshalling
|
|
require.Equal(t, jsonMarshalledOriginalBz, jsonMarshalledUpdatedBz)
|
|
|
|
// raw build also make same result
|
|
jsonMarshalExpectedResponse, err := appCodec.MarshalJSON(tc.expectedProto())
|
|
require.NoError(t, err)
|
|
require.Equal(t, jsonMarshalledUpdatedBz, jsonMarshalExpectedResponse)
|
|
})
|
|
}
|
|
}
|