Merge pull request #22 from cosmwasm/improve_contract_queries_12
Improve wasm contract queries
This commit is contained in:
@@ -37,7 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
## [Unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
* (wasmd)[\#2](https://github.com/cosmwasm/wasmd/pull/22) Improve wasm contract queries (all, raw, smart)
|
||||
* (wasmd) [\#119](https://github.com/cosmwasm/wasmd/pull/119) Add support for the `--inter-block-cache` CLI
|
||||
flag and configuration.
|
||||
* (wasmcli) [\#132](https://github.com/cosmwasm/wasmd/pull/132) Add `tx decode` command to decode
|
||||
|
||||
@@ -95,7 +95,7 @@ sleep 3
|
||||
wasmcli query wasm list-contracts
|
||||
CONTRACT=cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5
|
||||
wasmcli query wasm contract $CONTRACT
|
||||
wasmcli query wasm contract-state $CONTRACT
|
||||
wasmcli query wasm contract-state all $CONTRACT
|
||||
wasmcli query account $CONTRACT
|
||||
|
||||
# execute fails if wrong person
|
||||
|
||||
1
go.mod
1
go.mod
@@ -19,6 +19,7 @@ require (
|
||||
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.5.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/tendermint/go-amino v0.15.1
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@@ -145,10 +150,27 @@ func GetCmdGetContractInfo(cdc *codec.Codec) *cobra.Command {
|
||||
|
||||
// GetCmdGetContractState dumps full internal state of a given contract
|
||||
func GetCmdGetContractState(cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "contract-state",
|
||||
Short: "Querying commands for the wasm module",
|
||||
DisableFlagParsing: true,
|
||||
SuggestionsMinimumDistance: 2,
|
||||
RunE: client.ValidateCmd,
|
||||
}
|
||||
cmd.AddCommand(client.GetCommands(
|
||||
GetCmdGetContractStateAll(cdc),
|
||||
GetCmdGetContractStateRaw(cdc),
|
||||
GetCmdGetContractStateSmart(cdc),
|
||||
)...)
|
||||
return cmd
|
||||
|
||||
}
|
||||
|
||||
func GetCmdGetContractStateAll(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "contract-state [bech32_address]",
|
||||
Short: "Prints out internal state of a contract given its address",
|
||||
Long: "Prints out internal state of a contract given its address",
|
||||
Use: "all [bech32_address]",
|
||||
Short: "Prints out all internal state of a contract given its address",
|
||||
Long: "Prints out all internal state of a contract given its address",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
@@ -158,7 +180,7 @@ func GetCmdGetContractState(cdc *codec.Codec) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String())
|
||||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateAll)
|
||||
res, _, err := cliCtx.Query(route)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -168,3 +190,114 @@ func GetCmdGetContractState(cdc *codec.Codec) *cobra.Command {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetCmdGetContractStateRaw(cdc *codec.Codec) *cobra.Command {
|
||||
decoder := newArgDecoder(hex.DecodeString)
|
||||
cmd := &cobra.Command{
|
||||
Use: "raw [bech32_address] [key]",
|
||||
Short: "Prints out internal state for key of a contract given its address",
|
||||
Long: "Prints out internal state for of a contract given its address",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
queryData, err := decoder.DecodeString(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateRaw)
|
||||
res, _, err := cliCtx.QueryWithData(route, queryData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(res))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
decoder.RegisterFlags(cmd.PersistentFlags(), "key argument")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func GetCmdGetContractStateSmart(cdc *codec.Codec) *cobra.Command {
|
||||
decoder := newArgDecoder(asciiDecodeString)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "smart [bech32_address] [query]",
|
||||
Short: "Calls contract with given address with query data and prints the returned result",
|
||||
Long: "Calls contract with given address with query data and prints the returned result",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := args[1]
|
||||
if key == "" {
|
||||
return errors.New("key must not be empty")
|
||||
}
|
||||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateSmart)
|
||||
|
||||
queryData, err := decoder.DecodeString(args[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode query: %s", err)
|
||||
}
|
||||
res, _, err := cliCtx.QueryWithData(route, queryData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(res))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
decoder.RegisterFlags(cmd.PersistentFlags(), "query argument")
|
||||
return cmd
|
||||
}
|
||||
|
||||
type argumentDecoder struct {
|
||||
// dec is the default decoder
|
||||
dec func(string) ([]byte, error)
|
||||
asciiF, hexF, b64F bool
|
||||
}
|
||||
|
||||
func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
|
||||
return &argumentDecoder{dec: def}
|
||||
}
|
||||
|
||||
func (a *argumentDecoder) RegisterFlags(f *flag.FlagSet, argName string) {
|
||||
f.BoolVar(&a.asciiF, "ascii", false, "ascii encoded "+argName)
|
||||
f.BoolVar(&a.hexF, "hex", false, "hex encoded "+argName)
|
||||
f.BoolVar(&a.b64F, "b64", false, "base64 encoded "+argName)
|
||||
}
|
||||
|
||||
func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
|
||||
found := -1
|
||||
for i, v := range []*bool{&a.asciiF, &a.hexF, &a.b64F} {
|
||||
if !*v {
|
||||
continue
|
||||
}
|
||||
if found != -1 {
|
||||
return nil, errors.New("multiple decoding flags used")
|
||||
}
|
||||
found = i
|
||||
}
|
||||
switch found {
|
||||
case 0:
|
||||
return asciiDecodeString(s)
|
||||
case 1:
|
||||
return hex.DecodeString(s)
|
||||
case 2:
|
||||
return base64.StdEncoding.DecodeString(s)
|
||||
default:
|
||||
return a.dec(s)
|
||||
}
|
||||
}
|
||||
|
||||
func asciiDecodeString(s string) ([]byte, error) {
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ const GasMultiplier = 100
|
||||
// MaxGas for a contract is 900 million (enforced in rust)
|
||||
const MaxGas = 900_000_000
|
||||
|
||||
const smartQueryGasLimit = 3000000 // Todo: should be set by app.toml
|
||||
|
||||
// Keeper will have a reference to Wasmer with it's own data directory.
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
@@ -37,6 +39,8 @@ type Keeper struct {
|
||||
router sdk.Router
|
||||
|
||||
wasmer wasm.Wasmer
|
||||
// queryGasLimit is the max wasm gas that can be spent on executing a query with a contract
|
||||
queryGasLimit uint64
|
||||
}
|
||||
|
||||
// NewKeeper creates a new contract Keeper instance
|
||||
@@ -53,6 +57,7 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou
|
||||
accountKeeper: accountKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
router: router,
|
||||
queryGasLimit: smartQueryGasLimit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,22 +137,10 @@ func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint
|
||||
|
||||
// Execute executes the contract instance
|
||||
func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, coins sdk.Coins, msgs []byte) (sdk.Result, sdk.Error) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
contractBz := store.Get(types.GetContractAddressKey(contractAddress))
|
||||
if contractBz == nil {
|
||||
return sdk.Result{}, types.ErrNotFound("contract")
|
||||
codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress)
|
||||
if err != nil {
|
||||
return sdk.Result{}, err
|
||||
}
|
||||
var contract types.ContractInfo
|
||||
k.cdc.MustUnmarshalBinaryBare(contractBz, &contract)
|
||||
|
||||
contractInfoBz := store.Get(types.GetCodeKey(contract.CodeID))
|
||||
if contractInfoBz == nil {
|
||||
return sdk.Result{}, types.ErrNotFound("contract info")
|
||||
}
|
||||
var codeInfo types.CodeInfo
|
||||
k.cdc.MustUnmarshalBinaryBare(contractInfoBz, &codeInfo)
|
||||
|
||||
// add more funds
|
||||
sdkerr := k.bankKeeper.SendCoins(ctx, caller, contractAddress, coins)
|
||||
if sdkerr != nil {
|
||||
@@ -156,13 +149,10 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
||||
contractAccount := k.accountKeeper.GetAccount(ctx, contractAddress)
|
||||
params := types.NewParams(ctx, caller, coins, contractAccount)
|
||||
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
|
||||
gas := gasForContract(ctx)
|
||||
res, err := k.wasmer.Execute(codeInfo.CodeHash, params, msgs, prefixStore, gas)
|
||||
if err != nil {
|
||||
return sdk.Result{}, types.ErrExecuteFailed(err)
|
||||
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msgs, prefixStore, gas)
|
||||
if execErr != nil {
|
||||
return sdk.Result{}, types.ErrExecuteFailed(execErr)
|
||||
}
|
||||
consumeGas(ctx, res.GasUsed)
|
||||
|
||||
@@ -174,6 +164,68 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
||||
return types.CosmosResult(*res), nil
|
||||
}
|
||||
|
||||
// QuerySmart queries the smart contract itself.
|
||||
func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]types.Model, sdk.Error) {
|
||||
ctx = ctx.WithGasMeter(sdk.NewGasMeter(k.queryGasLimit))
|
||||
|
||||
codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, gasForContract(ctx))
|
||||
if qErr != nil {
|
||||
return nil, types.ErrExecuteFailed(qErr)
|
||||
}
|
||||
consumeGas(ctx, gasUsed)
|
||||
models := make([]types.Model, len(queryResult.Results))
|
||||
for i := range queryResult.Results {
|
||||
models[i] = types.Model{
|
||||
Key: queryResult.Results[i].Key,
|
||||
Value: string(queryResult.Results[i].Value),
|
||||
}
|
||||
}
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// QueryRaw returns the contract's state for give key. For a `nil` key a empty slice` result is returned.
|
||||
func (k Keeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []types.Model {
|
||||
result := make([]types.Model, 0)
|
||||
if key == nil {
|
||||
return result
|
||||
}
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
|
||||
if val := prefixStore.Get(key); val != nil {
|
||||
return append(result, types.Model{
|
||||
Key: string(key),
|
||||
Value: string(val),
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.CodeInfo, prefix.Store, sdk.Error) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
contractBz := store.Get(types.GetContractAddressKey(contractAddress))
|
||||
if contractBz == nil {
|
||||
return types.CodeInfo{}, prefix.Store{}, types.ErrNotFound("contract")
|
||||
}
|
||||
var contract types.ContractInfo
|
||||
k.cdc.MustUnmarshalBinaryBare(contractBz, &contract)
|
||||
|
||||
contractInfoBz := store.Get(types.GetCodeKey(contract.CodeID))
|
||||
if contractInfoBz == nil {
|
||||
return types.CodeInfo{}, prefix.Store{}, types.ErrNotFound("contract info")
|
||||
}
|
||||
var codeInfo types.CodeInfo
|
||||
k.cdc.MustUnmarshalBinaryBare(contractInfoBz, &codeInfo)
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
return codeInfo, prefixStore, nil
|
||||
}
|
||||
|
||||
func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
var contract types.ContractInfo
|
||||
|
||||
@@ -20,6 +20,12 @@ const (
|
||||
QueryListCode = "list-code"
|
||||
)
|
||||
|
||||
const (
|
||||
QueryMethodContractStateSmart = "smart"
|
||||
QueryMethodContractStateAll = "all"
|
||||
QueryMethodContractStateRaw = "raw"
|
||||
)
|
||||
|
||||
// NewQuerier creates a new querier
|
||||
func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
|
||||
@@ -29,7 +35,10 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
case QueryListContracts:
|
||||
return queryContractList(ctx, req, keeper)
|
||||
case QueryGetContractState:
|
||||
return queryContractState(ctx, path[1], req, keeper)
|
||||
if len(path) < 3 {
|
||||
return nil, sdk.ErrUnknownRequest("unknown data query endpoint")
|
||||
}
|
||||
return queryContractState(ctx, path[1], path[2], req, keeper)
|
||||
case QueryGetCode:
|
||||
return queryCode(ctx, path[1], req, keeper)
|
||||
case QueryListCode:
|
||||
@@ -67,23 +76,36 @@ func queryContractList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryContractState(ctx sdk.Context, bech string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr, err := sdk.AccAddressFromBech32(bech)
|
||||
func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
|
||||
contractAddr, err := sdk.AccAddressFromBech32(bech)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrUnknownRequest(err.Error())
|
||||
}
|
||||
iter := keeper.GetContractState(ctx, addr)
|
||||
|
||||
var state []types.Model
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
m := types.Model{
|
||||
Key: string(iter.Key()),
|
||||
Value: string(iter.Value()),
|
||||
var resultData []types.Model
|
||||
switch queryMethod {
|
||||
case QueryMethodContractStateAll:
|
||||
for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() {
|
||||
resultData = append(resultData, types.Model{
|
||||
Key: string(iter.Key()),
|
||||
Value: string(iter.Value()),
|
||||
})
|
||||
}
|
||||
state = append(state, m)
|
||||
if resultData == nil {
|
||||
resultData = make([]types.Model, 0)
|
||||
}
|
||||
case QueryMethodContractStateRaw:
|
||||
resultData = keeper.QueryRaw(ctx, contractAddr, req.Data)
|
||||
case QueryMethodContractStateSmart:
|
||||
res, err := keeper.QuerySmart(ctx, contractAddr, req.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultData = res
|
||||
default:
|
||||
return nil, sdk.ErrUnknownRequest("unsupported data query method for contract-state")
|
||||
}
|
||||
|
||||
bz, err := json.MarshalIndent(state, "", " ")
|
||||
bz, err := json.MarshalIndent(resultData, "", " ")
|
||||
if err != nil {
|
||||
return nil, sdk.ErrUnknownRequest(err.Error())
|
||||
}
|
||||
|
||||
131
x/wasm/internal/keeper/querier_test.go
Normal file
131
x/wasm/internal/keeper/querier_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
func TestQueryContractState(t *testing.T) {
|
||||
type model struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"val"`
|
||||
}
|
||||
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit))
|
||||
anyAddr := createFakeFundedAccount(ctx, accKeeper, topUp)
|
||||
|
||||
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
|
||||
require.NoError(t, err)
|
||||
|
||||
contractID, err := keeper.Create(ctx, creator, wasmCode)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, bob := keyPubAddr()
|
||||
initMsg := InitMsg{
|
||||
Verifier: anyAddr.String(),
|
||||
Beneficiary: bob.String(),
|
||||
}
|
||||
initMsgBz, err := json.Marshal(initMsg)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, err := keeper.Instantiate(ctx, creator, contractID, initMsgBz, deposit)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String())
|
||||
|
||||
contractModel := []types.Model{
|
||||
{Key: "foo", Value: "bar"},
|
||||
{Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})},
|
||||
}
|
||||
keeper.setContractState(ctx, addr, contractModel)
|
||||
|
||||
q := NewQuerier(keeper)
|
||||
specs := map[string]struct {
|
||||
srcPath []string
|
||||
srcReq abci.RequestQuery
|
||||
expModelLen int
|
||||
expModelContains []model
|
||||
expErr sdk.Error
|
||||
}{
|
||||
"query all": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateAll},
|
||||
expModelLen: 3,
|
||||
expModelContains: []model{
|
||||
{Key: "foo", Value: "bar"},
|
||||
{Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})},
|
||||
},
|
||||
},
|
||||
"query raw key": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
|
||||
srcReq: abci.RequestQuery{Data: []byte("foo")},
|
||||
expModelLen: 1,
|
||||
expModelContains: []model{{Key: "foo", Value: "bar"}},
|
||||
},
|
||||
"query raw binary key": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
|
||||
srcReq: abci.RequestQuery{Data: []byte{0x0, 0x1}},
|
||||
expModelLen: 1,
|
||||
expModelContains: []model{{Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})}},
|
||||
},
|
||||
"query smart": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
|
||||
srcReq: abci.RequestQuery{Data: []byte(`{"raw":{"key":"config"}}`)},
|
||||
expModelLen: 1,
|
||||
//expModelContains: []model{}, // stopping here as contract internals are not stable
|
||||
},
|
||||
"query unknown raw key": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
|
||||
srcReq: abci.RequestQuery{Data: []byte("unknown")},
|
||||
expModelLen: 0,
|
||||
},
|
||||
"query empty raw key": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
|
||||
expModelLen: 0,
|
||||
},
|
||||
"query raw with unknown address": {
|
||||
srcPath: []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateRaw},
|
||||
expModelLen: 0,
|
||||
},
|
||||
"query all with unknown address": {
|
||||
srcPath: []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateAll},
|
||||
expModelLen: 0,
|
||||
},
|
||||
"query smart with unknown address": {
|
||||
srcPath: []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateSmart},
|
||||
expModelLen: 0,
|
||||
expErr: types.ErrNotFound("contract"),
|
||||
},
|
||||
}
|
||||
|
||||
for msg, spec := range specs {
|
||||
t.Run(msg, func(t *testing.T) {
|
||||
binResult, err := q(ctx, spec.srcPath, spec.srcReq)
|
||||
require.Equal(t, spec.expErr, err)
|
||||
// then
|
||||
var r []model
|
||||
if spec.expErr == nil {
|
||||
require.NoError(t, json.Unmarshal(binResult, &r))
|
||||
require.NotNil(t, r)
|
||||
}
|
||||
require.Len(t, r, spec.expModelLen)
|
||||
// and in result set
|
||||
for _, v := range spec.expModelContains {
|
||||
assert.Contains(t, r, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// Model is a struct that holds a KV pair
|
||||
type Model struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Value string `json:"val"`
|
||||
}
|
||||
|
||||
// CodeInfo is data for the uploaded contract WASM code
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmwasm/wasmd/x/wasm/internal/keeper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -395,11 +396,11 @@ func assertContractList(t *testing.T, q sdk.Querier, ctx sdk.Context, addrs []st
|
||||
|
||||
type model struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Value string `json:"val"`
|
||||
}
|
||||
|
||||
func assertContractState(t *testing.T, q sdk.Querier, ctx sdk.Context, addr sdk.AccAddress, expected state) {
|
||||
path := []string{QueryGetContractState, addr.String()}
|
||||
path := []string{QueryGetContractState, addr.String(), keeper.QueryMethodContractStateAll}
|
||||
bz, sdkerr := q(ctx, path, abci.RequestQuery{})
|
||||
require.NoError(t, sdkerr)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user