laconicd-deprecated/x/wasm/keeper/query_plugins_test.go
2023-02-28 15:05:49 +05:30

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)
})
}
}