mirror of https://github.com/certusone/wasmd.git
1163 lines
42 KiB
Go
1163 lines
42 KiB
Go
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types"
|
|
"github.com/cometbft/cometbft/crypto/ed25519"
|
|
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
|
"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"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
sdkmath "cosmossdk.io/math"
|
|
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
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"
|
|
|
|
"github.com/CosmWasm/wasmd/app"
|
|
"github.com/CosmWasm/wasmd/x/wasm/keeper"
|
|
wasmKeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
|
|
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
|
|
"github.com/CosmWasm/wasmd/x/wasm/types"
|
|
)
|
|
|
|
func TestMaskReflectCustomQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...)
|
|
|
|
// upload code
|
|
codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), codeID)
|
|
|
|
// creator instantiates a contract and gives it tokens
|
|
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, contractAddr)
|
|
|
|
// let's perform a normal query of state
|
|
ownerQuery := testdata.ReflectQueryMsg{
|
|
Owner: &struct{}{},
|
|
}
|
|
ownerQueryBz, err := json.Marshal(ownerQuery)
|
|
require.NoError(t, err)
|
|
ownerRes, err := keeper.QuerySmart(ctx, contractAddr, ownerQueryBz)
|
|
require.NoError(t, err)
|
|
var res testdata.OwnerResponse
|
|
err = json.Unmarshal(ownerRes, &res)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, res.Owner, creator.String())
|
|
|
|
// and now making use of the custom querier callbacks
|
|
customQuery := testdata.ReflectQueryMsg{
|
|
Capitalized: &testdata.Text{
|
|
Text: "all Caps noW",
|
|
},
|
|
}
|
|
customQueryBz, err := json.Marshal(customQuery)
|
|
require.NoError(t, err)
|
|
custom, err := keeper.QuerySmart(ctx, contractAddr, customQueryBz)
|
|
require.NoError(t, err)
|
|
var resp capitalizedResponse
|
|
err = json.Unmarshal(custom, &resp)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, resp.Text, "ALL CAPS NOW")
|
|
}
|
|
|
|
func TestReflectStargateQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
|
|
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
|
expectedBalance := funds.Sub(contractStart...)
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, funds...)
|
|
|
|
// upload code
|
|
codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), codeID)
|
|
|
|
// creator instantiates a contract and gives it tokens
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, contractAddr)
|
|
|
|
// first, normal query for the bank balance (to make sure our query is proper)
|
|
bankQuery := wasmvmtypes.QueryRequest{
|
|
Bank: &wasmvmtypes.BankQuery{
|
|
AllBalances: &wasmvmtypes.AllBalancesQuery{
|
|
Address: creator.String(),
|
|
},
|
|
},
|
|
}
|
|
simpleQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{Request: &bankQuery},
|
|
})
|
|
require.NoError(t, err)
|
|
simpleRes, err := keeper.QuerySmart(ctx, contractAddr, simpleQueryBz)
|
|
require.NoError(t, err)
|
|
var simpleChain testdata.ChainResponse
|
|
mustUnmarshal(t, simpleRes, &simpleChain)
|
|
var simpleBalance wasmvmtypes.AllBalancesResponse
|
|
mustUnmarshal(t, simpleChain.Data, &simpleBalance)
|
|
require.Equal(t, len(expectedBalance), len(simpleBalance.Amount))
|
|
assert.Equal(t, simpleBalance.Amount[0].Amount, expectedBalance[0].Amount.String())
|
|
assert.Equal(t, simpleBalance.Amount[0].Denom, expectedBalance[0].Denom)
|
|
}
|
|
|
|
func TestReflectGrpcQuery(t *testing.T) {
|
|
queryPlugins := (*reflectPlugins()).Merge(&wasmKeeper.QueryPlugins{
|
|
Grpc: func(ctx sdk.Context, request *wasmvmtypes.GrpcQuery) (proto.Message, error) {
|
|
if request.Path == "cosmos.bank.v1beta1.Query/AllBalances" {
|
|
return &banktypes.QueryAllBalancesResponse{
|
|
Balances: sdk.NewCoins(),
|
|
}, nil
|
|
}
|
|
return nil, errors.New("unsupported request")
|
|
},
|
|
})
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(&queryPlugins))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
creator := wasmKeeper.RandomAccountAddress(t)
|
|
|
|
// upload code
|
|
codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), codeID)
|
|
|
|
// creator instantiates a contract
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", sdk.NewCoins())
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, contractAddr)
|
|
|
|
// now grpc query for the bank balance
|
|
cosmosBankQuery := banktypes.NewQueryAllBalancesRequest(creator, nil, false)
|
|
cosmosBankQueryBz, err := proto.Marshal(cosmosBankQuery)
|
|
require.NoError(t, err)
|
|
reflectQuery := wasmvmtypes.QueryRequest{
|
|
Grpc: &wasmvmtypes.GrpcQuery{
|
|
Path: "cosmos.bank.v1beta1.Query/AllBalances",
|
|
Data: cosmosBankQueryBz,
|
|
},
|
|
}
|
|
reflectQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{Request: &reflectQuery},
|
|
})
|
|
require.NoError(t, err)
|
|
// query the reflect contract
|
|
reflectRespBz, err := keeper.QuerySmart(ctx, contractAddr, reflectQueryBz)
|
|
require.NoError(t, err)
|
|
var reflectResp testdata.ChainResponse
|
|
mustUnmarshal(t, reflectRespBz, &reflectResp)
|
|
// now unmarshal the protobuf response
|
|
var grpcBalance banktypes.QueryAllBalancesResponse
|
|
err = proto.Unmarshal(reflectResp.Data, &grpcBalance)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestReflectTotalSupplyQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
// upload code
|
|
codeID := wasmKeeper.StoreReflectContract(t, ctx, keepers).CodeID
|
|
// creator instantiates a contract and gives it tokens
|
|
creator := wasmKeeper.RandomAccountAddress(t)
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "testing", nil)
|
|
require.NoError(t, err)
|
|
|
|
currentStakeSupply := keepers.BankKeeper.GetSupply(ctx, "stake")
|
|
require.NotEmpty(t, currentStakeSupply.Amount) // ensure we have real data
|
|
specs := map[string]struct {
|
|
denom string
|
|
expAmount wasmvmtypes.Coin
|
|
}{
|
|
"known denom": {
|
|
denom: "stake",
|
|
expAmount: wasmKeeper.ConvertSdkCoinToWasmCoin(currentStakeSupply),
|
|
},
|
|
"unknown denom": {
|
|
denom: "unknown",
|
|
expAmount: wasmvmtypes.Coin{Denom: "unknown", Amount: "0"},
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
// when
|
|
queryBz := mustMarshal(t, testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{
|
|
Request: &wasmvmtypes.QueryRequest{
|
|
Bank: &wasmvmtypes.BankQuery{
|
|
Supply: &wasmvmtypes.SupplyQuery{Denom: spec.denom},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
simpleRes, err := keeper.QuerySmart(ctx, contractAddr, queryBz)
|
|
|
|
// then
|
|
require.NoError(t, err)
|
|
var rsp testdata.ChainResponse
|
|
mustUnmarshal(t, simpleRes, &rsp)
|
|
var supplyRsp wasmvmtypes.SupplyResponse
|
|
mustUnmarshal(t, rsp.Data, &supplyRsp)
|
|
assert.Equal(t, spec.expAmount, supplyRsp.Amount, spec.expAmount)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReflectInvalidStargateQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
|
|
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, funds...)
|
|
|
|
// upload code
|
|
codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), codeID)
|
|
|
|
// creator instantiates a contract and gives it tokens
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, contractAddr)
|
|
|
|
// now, try to build a protobuf query
|
|
protoQuery := banktypes.QueryAllBalancesRequest{
|
|
Address: creator.String(),
|
|
}
|
|
protoQueryBin, err := proto.Marshal(&protoQuery)
|
|
require.NoError(t, err)
|
|
|
|
protoRequest := wasmvmtypes.QueryRequest{
|
|
Stargate: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.bank.v1beta1.Query/AllBalances",
|
|
Data: protoQueryBin,
|
|
},
|
|
}
|
|
protoQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{Request: &protoRequest},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// make a query on the chain, should not be whitelisted
|
|
_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Unsupported query")
|
|
|
|
// now, try to build a protobuf query
|
|
protoRequest = wasmvmtypes.QueryRequest{
|
|
Stargate: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.tx.v1beta1.Service/GetTx",
|
|
Data: []byte{},
|
|
},
|
|
}
|
|
protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{Request: &protoRequest},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// make a query on the chain, should be blacklisted
|
|
_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Unsupported query")
|
|
|
|
// and another one
|
|
protoRequest = wasmvmtypes.QueryRequest{
|
|
Stargate: &wasmvmtypes.StargateQuery{
|
|
Path: "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo",
|
|
Data: []byte{},
|
|
},
|
|
}
|
|
protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{Request: &protoRequest},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// make a query on the chain, should be blacklisted
|
|
_, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Unsupported query")
|
|
}
|
|
|
|
type reflectState struct {
|
|
Owner string `json:"owner"`
|
|
}
|
|
|
|
func TestMaskReflectWasmQueries(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...)
|
|
|
|
// upload reflect code
|
|
reflectID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), reflectID)
|
|
|
|
// creator instantiates a contract and gives it tokens
|
|
reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
|
reflectAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, reflectAddr)
|
|
|
|
// for control, let's make some queries directly on the reflect
|
|
ownerQuery := buildReflectQuery(t, &testdata.ReflectQueryMsg{Owner: &struct{}{}})
|
|
res, err := keeper.QuerySmart(ctx, reflectAddr, ownerQuery)
|
|
require.NoError(t, err)
|
|
var ownerRes testdata.OwnerResponse
|
|
mustUnmarshal(t, res, &ownerRes)
|
|
require.Equal(t, ownerRes.Owner, creator.String())
|
|
|
|
// and a raw query: cosmwasm_storage::Singleton uses 2 byte big-endian length-prefixed to store data
|
|
configKey := append([]byte{0, 6}, []byte("config")...)
|
|
raw := keeper.QueryRaw(ctx, reflectAddr, configKey)
|
|
var stateRes reflectState
|
|
mustUnmarshal(t, raw, &stateRes)
|
|
require.Equal(t, stateRes.Owner, creator.String())
|
|
|
|
// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
|
|
reflectOwnerQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
|
|
Smart: &wasmvmtypes.SmartQuery{
|
|
ContractAddr: reflectAddr.String(),
|
|
Msg: ownerQuery,
|
|
},
|
|
}}}}
|
|
reflectOwnerBin := buildReflectQuery(t, &reflectOwnerQuery)
|
|
res, err = keeper.QuerySmart(ctx, reflectAddr, reflectOwnerBin)
|
|
require.NoError(t, err)
|
|
// first we pull out the data from chain response, before parsing the original response
|
|
var reflectRes testdata.ChainResponse
|
|
mustUnmarshal(t, res, &reflectRes)
|
|
var reflectOwnerRes testdata.OwnerResponse
|
|
mustUnmarshal(t, reflectRes.Data, &reflectOwnerRes)
|
|
require.Equal(t, reflectOwnerRes.Owner, creator.String())
|
|
|
|
// and with queryRaw
|
|
reflectStateQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
|
|
Raw: &wasmvmtypes.RawQuery{
|
|
ContractAddr: reflectAddr.String(),
|
|
Key: configKey,
|
|
},
|
|
}}}}
|
|
reflectStateBin := buildReflectQuery(t, &reflectStateQuery)
|
|
res, err = keeper.QuerySmart(ctx, reflectAddr, reflectStateBin)
|
|
require.NoError(t, err)
|
|
// first we pull out the data from chain response, before parsing the original response
|
|
var reflectRawRes testdata.ChainResponse
|
|
mustUnmarshal(t, res, &reflectRawRes)
|
|
// now, with the raw data, we can parse it into state
|
|
var reflectStateRes reflectState
|
|
mustUnmarshal(t, reflectRawRes.Data, &reflectStateRes)
|
|
require.Equal(t, reflectStateRes.Owner, creator.String())
|
|
}
|
|
|
|
func TestWasmRawQueryWithNil(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...)
|
|
|
|
// upload reflect code
|
|
reflectID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), reflectID)
|
|
|
|
// creator instantiates a contract and gives it tokens
|
|
reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
|
reflectAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, reflectAddr)
|
|
|
|
// control: query directly
|
|
missingKey := []byte{0, 1, 2, 3, 4}
|
|
raw := keeper.QueryRaw(ctx, reflectAddr, missingKey)
|
|
require.Nil(t, raw)
|
|
|
|
// and with queryRaw
|
|
reflectQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
|
|
Raw: &wasmvmtypes.RawQuery{
|
|
ContractAddr: reflectAddr.String(),
|
|
Key: missingKey,
|
|
},
|
|
}}}}
|
|
reflectStateBin := buildReflectQuery(t, &reflectQuery)
|
|
res, err := keeper.QuerySmart(ctx, reflectAddr, reflectStateBin)
|
|
require.NoError(t, err)
|
|
|
|
// first we pull out the data from chain response, before parsing the original response
|
|
var reflectRawRes testdata.ChainResponse
|
|
mustUnmarshal(t, res, &reflectRawRes)
|
|
// and make sure there is no data
|
|
require.Empty(t, reflectRawRes.Data)
|
|
// we get an empty byte slice not nil (if anyone care in go-land)
|
|
require.Equal(t, []byte{}, reflectRawRes.Data)
|
|
}
|
|
|
|
func TestQueryDenomsIntegration(t *testing.T) {
|
|
ctx, keepers := wasmKeeper.CreateTestInput(t, false, CyberpunkCapabilities)
|
|
ck, k := keepers.ContractKeeper, keepers.WasmKeeper
|
|
creator := keepers.Faucet.NewFundedRandomAccount(ctx, sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))...)
|
|
|
|
// upload code
|
|
codeID, _, err := ck.Create(ctx, creator, testdata.CyberpunkContractWasm(), nil)
|
|
require.NoError(t, err)
|
|
|
|
contractAddr, _, err := ck.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "cyberpunk contract", nil)
|
|
require.NoError(t, err)
|
|
|
|
var (
|
|
metadata1 = banktypes.Metadata{
|
|
Description: "testing",
|
|
DenomUnits: []*banktypes.DenomUnit{
|
|
{Denom: "ualx", Exponent: 0, Aliases: []string{"microalx"}},
|
|
{Denom: "alx", Exponent: 6, Aliases: []string{"ALX"}},
|
|
},
|
|
Base: "ualx",
|
|
Display: "alx",
|
|
Name: "my test denom",
|
|
Symbol: "XALX",
|
|
URI: "https://example.com/ualx",
|
|
URIHash: "my_hash",
|
|
}
|
|
metadata2 = banktypes.Metadata{
|
|
Description: "testing2",
|
|
DenomUnits: []*banktypes.DenomUnit{
|
|
{Denom: "ublx", Exponent: 0, Aliases: []string{"microblx"}},
|
|
{Denom: "blx", Exponent: 6, Aliases: []string{"BLX"}},
|
|
},
|
|
Base: "ublx",
|
|
Display: "blx",
|
|
Name: "my other test denom",
|
|
Symbol: "XBLX",
|
|
}
|
|
)
|
|
type dict map[string]any
|
|
|
|
keepers.BankKeeper.SetDenomMetaData(ctx, metadata1)
|
|
keepers.BankKeeper.SetDenomMetaData(ctx, metadata2)
|
|
|
|
specs := map[string]struct {
|
|
query string
|
|
exp []byte
|
|
expErr *errorsmod.Error
|
|
}{
|
|
"all denoms": {
|
|
query: `{"denoms":{}}`,
|
|
exp: mustMarshal(t, []dict{
|
|
{
|
|
"description": "testing",
|
|
"denom_units": []dict{
|
|
{"denom": "ualx", "exponent": 0, "aliases": []string{"microalx"}},
|
|
{"denom": "alx", "exponent": 6, "aliases": []string{"ALX"}},
|
|
},
|
|
"base": "ualx",
|
|
"display": "alx",
|
|
"name": "my test denom",
|
|
"symbol": "XALX",
|
|
"uri": "https://example.com/ualx",
|
|
"uri_hash": "my_hash",
|
|
}, {
|
|
"description": "testing2",
|
|
"denom_units": []dict{
|
|
{"denom": "ublx", "exponent": 0, "aliases": []string{"microblx"}},
|
|
{"denom": "blx", "exponent": 6, "aliases": []string{"BLX"}},
|
|
},
|
|
"base": "ublx",
|
|
"display": "blx",
|
|
"name": "my other test denom",
|
|
"symbol": "XBLX",
|
|
"uri": "",
|
|
"uri_hash": "",
|
|
},
|
|
}),
|
|
},
|
|
"single denom": {
|
|
query: `{"denom":{"denom":"ublx"}}`,
|
|
exp: mustMarshal(t, dict{
|
|
"description": "testing2",
|
|
"denom_units": []dict{
|
|
{"denom": "ublx", "exponent": 0, "aliases": []string{"microblx"}},
|
|
{"denom": "blx", "exponent": 6, "aliases": []string{"BLX"}},
|
|
},
|
|
"base": "ublx",
|
|
"display": "blx",
|
|
"name": "my other test denom",
|
|
"symbol": "XBLX",
|
|
"uri": "",
|
|
"uri_hash": "",
|
|
}),
|
|
},
|
|
"unknown denom": {
|
|
query: `{"denom":{"denom":"unknown"}}`,
|
|
expErr: sdkerrors.ErrNotFound,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
gotData, gotErr := k.QuerySmart(ctx, contractAddr, []byte(spec.query))
|
|
if spec.expErr != nil {
|
|
require.Error(t, gotErr)
|
|
assert.Contains(t, gotErr.Error(), fmt.Sprintf("codespace: %s, code: %d:", spec.expErr.Codespace(), spec.expErr.ABCICode()))
|
|
return
|
|
}
|
|
require.NoError(t, gotErr)
|
|
assert.JSONEq(t, string(spec.exp), string(gotData), string(gotData))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDistributionQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
pCtx, keepers := wasmKeeper.CreateTestInput(t, false, ReflectCapabilities, wasmKeeper.WithMessageEncoders(reflectEncoders(cdc)), wasmKeeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
|
|
example := wasmKeeper.InstantiateReflectExampleContract(t, pCtx, keepers)
|
|
delegator := keepers.Faucet.NewFundedRandomAccount(pCtx, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100_000_000))...)
|
|
otherAddr := keepers.Faucet.NewFundedRandomAccount(pCtx, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100_000_000))...)
|
|
|
|
val1Addr := addValidator(t, pCtx, keepers.StakingKeeper, keepers.Faucet, sdk.NewInt64Coin(sdk.DefaultBondDenom, 10_000_000))
|
|
val2Addr := addValidator(t, pCtx, keepers.StakingKeeper, keepers.Faucet, sdk.NewInt64Coin(sdk.DefaultBondDenom, 20_000_000))
|
|
_ = val2Addr
|
|
pCtx = nextBlock(pCtx, keepers.StakingKeeper)
|
|
|
|
noopSetup := func(t *testing.T, ctx sdk.Context) sdk.Context { return ctx }
|
|
specs := map[string]struct {
|
|
setup func(t *testing.T, ctx sdk.Context) sdk.Context
|
|
query *wasmvmtypes.DistributionQuery
|
|
expErr bool
|
|
assert func(t *testing.T, d []byte)
|
|
}{
|
|
"delegator address - no withdrawal addr set": {
|
|
setup: noopSetup,
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegatorWithdrawAddress: &wasmvmtypes.DelegatorWithdrawAddressQuery{DelegatorAddress: delegator.String()},
|
|
},
|
|
assert: func(t *testing.T, d []byte) {
|
|
rsp := unmarshalReflect[wasmvmtypes.DelegatorWithdrawAddressResponse](t, d)
|
|
assert.Equal(t, delegator.String(), rsp.WithdrawAddress)
|
|
},
|
|
},
|
|
"delegator address - withdrawal addr set": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
require.NoError(t, keepers.DistKeeper.SetWithdrawAddr(ctx, delegator, otherAddr))
|
|
return ctx
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegatorWithdrawAddress: &wasmvmtypes.DelegatorWithdrawAddressQuery{DelegatorAddress: delegator.String()},
|
|
},
|
|
assert: func(t *testing.T, d []byte) {
|
|
var rsp wasmvmtypes.DelegatorWithdrawAddressResponse
|
|
mustUnmarshal(t, d, &rsp)
|
|
assert.Equal(t, otherAddr.String(), rsp.WithdrawAddress)
|
|
},
|
|
},
|
|
"delegator address - empty": {
|
|
setup: noopSetup,
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegatorWithdrawAddress: &wasmvmtypes.DelegatorWithdrawAddressQuery{},
|
|
},
|
|
expErr: true,
|
|
},
|
|
"delegation rewards - existing delegation": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
val1, err := keepers.StakingKeeper.GetValidator(ctx, val1Addr)
|
|
require.NoError(t, err)
|
|
_, err = keepers.StakingKeeper.Delegate(ctx, delegator, sdkmath.NewInt(10_000_000), stakingtypes.Unbonded, val1, true)
|
|
require.NoError(t, err)
|
|
setValidatorRewards(ctx, keepers.StakingKeeper, keepers.DistKeeper, val1Addr, "100000000")
|
|
return nextBlock(ctx, keepers.StakingKeeper)
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegationRewards: &wasmvmtypes.DelegationRewardsQuery{DelegatorAddress: delegator.String(), ValidatorAddress: val1Addr.String()},
|
|
},
|
|
assert: func(t *testing.T, d []byte) {
|
|
var rsp wasmvmtypes.DelegationRewardsResponse
|
|
mustUnmarshal(t, d, &rsp)
|
|
expRewards := []wasmvmtypes.DecCoin{{Amount: "45000000.000000000000000000", Denom: "stake"}}
|
|
assert.Equal(t, expRewards, rsp.Rewards)
|
|
},
|
|
},
|
|
"delegation rewards - no delegation": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
setValidatorRewards(ctx, keepers.StakingKeeper, keepers.DistKeeper, val1Addr, "100000000")
|
|
return nextBlock(ctx, keepers.StakingKeeper)
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegationRewards: &wasmvmtypes.DelegationRewardsQuery{DelegatorAddress: delegator.String(), ValidatorAddress: val1Addr.String()},
|
|
},
|
|
expErr: true,
|
|
},
|
|
"delegation rewards - validator empty": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
val, err := keepers.StakingKeeper.GetValidator(ctx, val1Addr)
|
|
require.NoError(t, err)
|
|
_, err = keepers.StakingKeeper.Delegate(ctx, delegator, sdkmath.NewInt(10_000_000), stakingtypes.Unbonded, val, true)
|
|
require.NoError(t, err)
|
|
return ctx
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegationRewards: &wasmvmtypes.DelegationRewardsQuery{DelegatorAddress: delegator.String()},
|
|
},
|
|
expErr: true,
|
|
},
|
|
"delegation total rewards": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
val, err := keepers.StakingKeeper.GetValidator(ctx, val1Addr)
|
|
require.NoError(t, err)
|
|
_, err = keepers.StakingKeeper.Delegate(ctx, delegator, sdkmath.NewInt(10_000_000), stakingtypes.Unbonded, val, true)
|
|
require.NoError(t, err)
|
|
setValidatorRewards(ctx, keepers.StakingKeeper, keepers.DistKeeper, val1Addr, "100000000")
|
|
return nextBlock(ctx, keepers.StakingKeeper)
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegationTotalRewards: &wasmvmtypes.DelegationTotalRewardsQuery{DelegatorAddress: delegator.String()},
|
|
},
|
|
assert: func(t *testing.T, d []byte) {
|
|
var rsp wasmvmtypes.DelegationTotalRewardsResponse
|
|
mustUnmarshal(t, d, &rsp)
|
|
expRewards := []wasmvmtypes.DelegatorReward{
|
|
{
|
|
Reward: []wasmvmtypes.DecCoin{{Amount: "45000000.000000000000000000", Denom: "stake"}},
|
|
ValidatorAddress: val1Addr.String(),
|
|
},
|
|
}
|
|
assert.Equal(t, expRewards, rsp.Rewards)
|
|
expTotal := []wasmvmtypes.DecCoin{{Amount: "45000000.000000000000000000", Denom: "stake"}}
|
|
assert.Equal(t, expTotal, rsp.Total)
|
|
},
|
|
},
|
|
"delegator validators": {
|
|
setup: func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
for _, v := range []sdk.ValAddress{val1Addr, val2Addr} {
|
|
val, err := keepers.StakingKeeper.GetValidator(ctx, v)
|
|
require.NoError(t, err)
|
|
_, err = keepers.StakingKeeper.Delegate(ctx, delegator, sdkmath.NewInt(10_000_000), stakingtypes.Unbonded, val, true)
|
|
require.NoError(t, err)
|
|
}
|
|
return ctx
|
|
},
|
|
query: &wasmvmtypes.DistributionQuery{
|
|
DelegatorValidators: &wasmvmtypes.DelegatorValidatorsQuery{DelegatorAddress: delegator.String()},
|
|
},
|
|
assert: func(t *testing.T, d []byte) {
|
|
var rsp wasmvmtypes.DelegatorValidatorsResponse
|
|
mustUnmarshal(t, d, &rsp)
|
|
expVals := []string{val1Addr.String(), val2Addr.String()}
|
|
if bytes.Compare(val1Addr, val2Addr) > 0 {
|
|
expVals = []string{expVals[1], expVals[0]}
|
|
}
|
|
assert.Equal(t, expVals, rsp.Validators)
|
|
},
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
ctx, _ := pCtx.CacheContext()
|
|
ctx = spec.setup(t, ctx)
|
|
|
|
// when
|
|
queryBz := mustMarshal(t, testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{
|
|
Request: &wasmvmtypes.QueryRequest{Distribution: spec.query},
|
|
},
|
|
})
|
|
simpleRes, gotErr := keeper.QuerySmart(ctx, example.Contract, queryBz)
|
|
if spec.expErr {
|
|
require.Error(t, gotErr)
|
|
return
|
|
}
|
|
// then
|
|
require.NoError(t, gotErr)
|
|
var rsp testdata.ChainResponse
|
|
mustUnmarshal(t, simpleRes, &rsp)
|
|
spec.assert(t, rsp.Data)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIBCListChannelsQuery(t *testing.T) {
|
|
cdc := wasmKeeper.MakeEncodingConfig(t).Codec
|
|
pCtx, keepers := keeper.CreateTestInput(t, false, ReflectCapabilities, keeper.WithMessageEncoders(reflectEncoders(cdc)), keeper.WithQueryPlugins(reflectPlugins()))
|
|
keeper := keepers.WasmKeeper
|
|
nonIbcExample := wasmKeeper.InstantiateReflectExampleContract(t, pCtx, keepers)
|
|
// add an ibc port for testing
|
|
myIBCPortID := "myValidPortID"
|
|
ibcExample := wasmKeeper.InstantiateReflectExampleContractWithPortID(t, pCtx, keepers, myIBCPortID)
|
|
|
|
// store a random channel to be ignored in queries
|
|
unusedChan := channeltypes.Channel{
|
|
State: channeltypes.OPEN,
|
|
Ordering: channeltypes.UNORDERED,
|
|
Counterparty: channeltypes.Counterparty{
|
|
PortId: "counterPartyPortID",
|
|
ChannelId: "counterPartyChannelID",
|
|
},
|
|
ConnectionHops: []string{"any"},
|
|
Version: "any",
|
|
}
|
|
keepers.IBCKeeper.ChannelKeeper.SetChannel(pCtx, "nonContractPortID", "channel-99", unusedChan)
|
|
|
|
// mixed channel examples for testing
|
|
myExampleChannels := []channeltypes.Channel{
|
|
{
|
|
State: channeltypes.OPEN,
|
|
Ordering: channeltypes.ORDERED,
|
|
Counterparty: channeltypes.Counterparty{
|
|
PortId: "counterPartyPortID",
|
|
ChannelId: "counterPartyChannelID",
|
|
},
|
|
ConnectionHops: []string{"one"},
|
|
Version: "v1",
|
|
},
|
|
{
|
|
State: channeltypes.INIT,
|
|
Ordering: channeltypes.UNORDERED,
|
|
Counterparty: channeltypes.Counterparty{
|
|
PortId: "foobar",
|
|
},
|
|
ConnectionHops: []string{"one"},
|
|
Version: "initversion",
|
|
},
|
|
{
|
|
State: channeltypes.OPEN,
|
|
Ordering: channeltypes.UNORDERED,
|
|
Counterparty: channeltypes.Counterparty{
|
|
PortId: "otherCounterPartyPortID",
|
|
ChannelId: "otherCounterPartyChannelID",
|
|
},
|
|
ConnectionHops: []string{"other", "second"},
|
|
Version: "otherVersion",
|
|
},
|
|
{
|
|
State: channeltypes.CLOSED,
|
|
Ordering: channeltypes.ORDERED,
|
|
Counterparty: channeltypes.Counterparty{
|
|
PortId: "super",
|
|
ChannelId: "duper",
|
|
},
|
|
ConnectionHops: []string{"no-more"},
|
|
Version: "closedVersion",
|
|
},
|
|
}
|
|
|
|
withChannelsStored := func(portID string, channels ...channeltypes.Channel) func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
return func(t *testing.T, ctx sdk.Context) sdk.Context {
|
|
for i, v := range channels {
|
|
keepers.IBCKeeper.ChannelKeeper.SetChannel(ctx, portID, fmt.Sprintf("channel-%d", i), v)
|
|
}
|
|
return ctx
|
|
}
|
|
}
|
|
noopSetup := func(t *testing.T, ctx sdk.Context) sdk.Context { return ctx }
|
|
|
|
specs := map[string]struct {
|
|
setup func(t *testing.T, ctx sdk.Context) sdk.Context
|
|
contract sdk.AccAddress
|
|
query *wasmvmtypes.IBCQuery
|
|
expErr bool
|
|
assert func(t *testing.T, d []byte)
|
|
}{
|
|
"open channels - with query portID empty": {
|
|
contract: ibcExample.Contract,
|
|
setup: withChannelsStored(myIBCPortID, myExampleChannels...),
|
|
query: &wasmvmtypes.IBCQuery{ListChannels: &wasmvmtypes.ListChannelsQuery{}},
|
|
assert: func(t *testing.T, d []byte) {
|
|
rsp := unmarshalReflect[wasmvmtypes.ListChannelsResponse](t, d)
|
|
exp := wasmvmtypes.ListChannelsResponse{Channels: []wasmvmtypes.IBCChannel{
|
|
{
|
|
Endpoint: wasmvmtypes.IBCEndpoint{PortID: myIBCPortID, ChannelID: "channel-0"},
|
|
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
|
|
PortID: "counterPartyPortID",
|
|
ChannelID: "counterPartyChannelID",
|
|
},
|
|
Order: channeltypes.ORDERED.String(),
|
|
Version: "v1",
|
|
ConnectionID: "one",
|
|
}, {
|
|
Endpoint: wasmvmtypes.IBCEndpoint{PortID: myIBCPortID, ChannelID: "channel-2"},
|
|
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
|
|
PortID: "otherCounterPartyPortID",
|
|
ChannelID: "otherCounterPartyChannelID",
|
|
},
|
|
Order: channeltypes.UNORDERED.String(),
|
|
Version: "otherVersion",
|
|
ConnectionID: "other",
|
|
},
|
|
}}
|
|
assert.Equal(t, exp, rsp)
|
|
},
|
|
},
|
|
"open channels - with query portID passed": {
|
|
contract: ibcExample.Contract,
|
|
setup: withChannelsStored("OtherPortID", myExampleChannels...),
|
|
query: &wasmvmtypes.IBCQuery{ListChannels: &wasmvmtypes.ListChannelsQuery{PortID: "OtherPortID"}},
|
|
assert: func(t *testing.T, d []byte) {
|
|
rsp := unmarshalReflect[wasmvmtypes.ListChannelsResponse](t, d)
|
|
exp := wasmvmtypes.ListChannelsResponse{Channels: []wasmvmtypes.IBCChannel{
|
|
{
|
|
Endpoint: wasmvmtypes.IBCEndpoint{PortID: "OtherPortID", ChannelID: "channel-0"},
|
|
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
|
|
PortID: "counterPartyPortID",
|
|
ChannelID: "counterPartyChannelID",
|
|
},
|
|
Order: channeltypes.ORDERED.String(),
|
|
Version: "v1",
|
|
ConnectionID: "one",
|
|
}, {
|
|
Endpoint: wasmvmtypes.IBCEndpoint{PortID: "OtherPortID", ChannelID: "channel-2"},
|
|
CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
|
|
PortID: "otherCounterPartyPortID",
|
|
ChannelID: "otherCounterPartyChannelID",
|
|
},
|
|
Order: channeltypes.UNORDERED.String(),
|
|
Version: "otherVersion",
|
|
ConnectionID: "other",
|
|
},
|
|
}}
|
|
assert.Equal(t, exp, rsp)
|
|
},
|
|
},
|
|
"non ibc contract - with query portID empty": {
|
|
contract: nonIbcExample.Contract,
|
|
setup: withChannelsStored(myIBCPortID, myExampleChannels...),
|
|
query: &wasmvmtypes.IBCQuery{ListChannels: &wasmvmtypes.ListChannelsQuery{}},
|
|
assert: func(t *testing.T, d []byte) {
|
|
rsp := unmarshalReflect[wasmvmtypes.ListChannelsResponse](t, d)
|
|
assert.Empty(t, rsp.Channels)
|
|
},
|
|
},
|
|
"no matching channels": {
|
|
contract: ibcExample.Contract,
|
|
setup: noopSetup,
|
|
query: &wasmvmtypes.IBCQuery{ListChannels: &wasmvmtypes.ListChannelsQuery{}},
|
|
assert: func(t *testing.T, d []byte) {
|
|
rsp := unmarshalReflect[wasmvmtypes.ListChannelsResponse](t, d)
|
|
assert.Empty(t, rsp.Channels)
|
|
},
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
ctx, _ := pCtx.CacheContext()
|
|
ctx = spec.setup(t, ctx)
|
|
|
|
// when
|
|
queryBz := mustMarshal(t, testdata.ReflectQueryMsg{
|
|
Chain: &testdata.ChainQuery{
|
|
Request: &wasmvmtypes.QueryRequest{IBC: spec.query},
|
|
},
|
|
})
|
|
simpleRes, gotErr := keeper.QuerySmart(ctx, spec.contract, queryBz)
|
|
if spec.expErr {
|
|
require.Error(t, gotErr)
|
|
return
|
|
}
|
|
// then
|
|
require.NoError(t, gotErr)
|
|
var rsp testdata.ChainResponse
|
|
mustUnmarshal(t, simpleRes, &rsp)
|
|
spec.assert(t, rsp.Data)
|
|
})
|
|
}
|
|
}
|
|
|
|
func unmarshalReflect[T any](t *testing.T, d []byte) T {
|
|
var v T
|
|
mustUnmarshal(t, d, &v)
|
|
return v
|
|
}
|
|
|
|
type reflectCustomQuery struct {
|
|
Ping *struct{} `json:"ping,omitempty"`
|
|
Capitalized *testdata.Text `json:"capitalized,omitempty"`
|
|
}
|
|
|
|
// this is from the go code back to the contract (capitalized or ping)
|
|
type customQueryResponse struct {
|
|
Msg string `json:"msg"`
|
|
}
|
|
|
|
// this is from the contract to the go code (capitalized or ping)
|
|
type capitalizedResponse struct {
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// reflectPlugins needs to be registered in test setup to handle custom query callbacks
|
|
func reflectPlugins() *wasmKeeper.QueryPlugins {
|
|
return &wasmKeeper.QueryPlugins{
|
|
Custom: performCustomQuery,
|
|
}
|
|
}
|
|
|
|
func performCustomQuery(_ sdk.Context, request json.RawMessage) ([]byte, error) {
|
|
var custom reflectCustomQuery
|
|
err := json.Unmarshal(request, &custom)
|
|
if err != nil {
|
|
return nil, errorsmod.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
|
}
|
|
if custom.Capitalized != nil {
|
|
msg := strings.ToUpper(custom.Capitalized.Text)
|
|
return json.Marshal(customQueryResponse{Msg: msg})
|
|
}
|
|
if custom.Ping != nil {
|
|
return json.Marshal(customQueryResponse{Msg: "pong"})
|
|
}
|
|
return nil, errorsmod.Wrap(types.ErrInvalidMsg, "Unknown Custom query variant")
|
|
}
|
|
|
|
func buildReflectQuery(t *testing.T, query *testdata.ReflectQueryMsg) []byte {
|
|
t.Helper()
|
|
bz, err := json.Marshal(query)
|
|
require.NoError(t, err)
|
|
return bz
|
|
}
|
|
|
|
func TestAcceptListStargateQuerier(t *testing.T) {
|
|
wasmApp := app.SetupWithEmptyStore(t)
|
|
ctx := wasmApp.NewUncachedContext(false, cmtproto.Header{ChainID: "foo", Height: 1, Time: time.Now()})
|
|
err := wasmApp.StakingKeeper.SetParams(ctx, stakingtypes.DefaultParams())
|
|
require.NoError(t, err)
|
|
|
|
addrs := app.AddTestAddrsIncremental(wasmApp, ctx, 2, sdkmath.NewInt(1_000_000))
|
|
accepted := wasmKeeper.AcceptedQueries{
|
|
"/cosmos.auth.v1beta1.Query/Account": func() proto.Message { return &authtypes.QueryAccountResponse{} },
|
|
"/no/route/to/this": func() proto.Message { return &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 := wasmKeeper.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))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResetProtoMarshalerAfterJsonMarshal(t *testing.T) {
|
|
appCodec := app.MakeEncodingConfig(t).Codec
|
|
|
|
protoMarshaler := &banktypes.QueryAllBalancesResponse{}
|
|
expected := appCodec.MustMarshalJSON(&banktypes.QueryAllBalancesResponse{
|
|
Balances: sdk.NewCoins(sdk.NewCoin("bar", sdkmath.NewInt(30))),
|
|
Pagination: &query.PageResponse{
|
|
NextKey: []byte("foo"),
|
|
},
|
|
})
|
|
|
|
bz, err := hex.DecodeString("0a090a036261721202333012050a03666f6f")
|
|
require.NoError(t, err)
|
|
|
|
// first marshal
|
|
response, err := wasmKeeper.ConvertProtoToJSONMarshal(appCodec, protoMarshaler, bz)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, response)
|
|
|
|
// second marshal
|
|
response, err = wasmKeeper.ConvertProtoToJSONMarshal(appCodec, protoMarshaler, bz)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, response)
|
|
}
|
|
|
|
// TestDeterministicJsonMarshal tests that we get deterministic JSON marshaled 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 proto.Message
|
|
expectedProto func() proto.Message
|
|
}{
|
|
/**
|
|
*
|
|
* 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() proto.Message {
|
|
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(t).Codec
|
|
|
|
originVersionBz, err := hex.DecodeString(tc.originalResponse)
|
|
require.NoError(t, err)
|
|
jsonMarshalledOriginalBz, err := wasmKeeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, originVersionBz)
|
|
require.NoError(t, err)
|
|
|
|
newVersionBz, err := hex.DecodeString(tc.updatedResponse)
|
|
require.NoError(t, err)
|
|
jsonMarshalledUpdatedBz, err := wasmKeeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, newVersionBz)
|
|
require.NoError(t, err)
|
|
|
|
// json marshaled 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)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvertProtoToJSONMarshal(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
queryPath string
|
|
protoResponseStruct proto.Message
|
|
originalResponse string
|
|
expectedProtoResponse proto.Message
|
|
expectedError bool
|
|
}{
|
|
{
|
|
name: "successful conversion from proto response to json marshaled response",
|
|
queryPath: "/cosmos.bank.v1beta1.Query/AllBalances",
|
|
originalResponse: "0a090a036261721202333012050a03666f6f",
|
|
protoResponseStruct: &banktypes.QueryAllBalancesResponse{},
|
|
expectedProtoResponse: &banktypes.QueryAllBalancesResponse{
|
|
Balances: sdk.NewCoins(sdk.NewCoin("bar", sdkmath.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(t).Codec
|
|
|
|
jsonMarshalledResponse, err := wasmKeeper.ConvertProtoToJSONMarshal(appCodec, tc.protoResponseStruct, originalVersionBz)
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// check response by json marshaling proto response into json response manually
|
|
jsonMarshalExpectedResponse, err := appCodec.MarshalJSON(tc.expectedProtoResponse)
|
|
require.NoError(t, err)
|
|
require.JSONEq(t, string(jsonMarshalledResponse), string(jsonMarshalExpectedResponse))
|
|
})
|
|
}
|
|
}
|