mirror of https://github.com/certusone/wasmd.git
Query contract state variations
This commit is contained in:
parent
c6aac95b69
commit
9156f29a72
|
@ -156,6 +156,7 @@ func GetCmdGetContractState(cdc *codec.Codec) *cobra.Command {
|
|||
cmd.AddCommand(client.GetCommands(
|
||||
GetCmdGetContractStateAll(cdc),
|
||||
GetCmdGetContractStateRaw(cdc),
|
||||
GetCmdGetContractStateSmart(cdc),
|
||||
)...)
|
||||
return cmd
|
||||
|
||||
|
@ -185,6 +186,7 @@ func GetCmdGetContractStateAll(cdc *codec.Codec) *cobra.Command {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetCmdGetContractStateRaw(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "raw [bech32_address] [key]",
|
||||
|
@ -202,8 +204,9 @@ func GetCmdGetContractStateRaw(cdc *codec.Codec) *cobra.Command {
|
|||
if key == "" {
|
||||
return errors.New("key must not be empty")
|
||||
}
|
||||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), key)
|
||||
res, _, err := cliCtx.Query(route)
|
||||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateRaw)
|
||||
queryData := []byte(key) // todo: open question: encode into json???
|
||||
res, _, err := cliCtx.QueryWithData(route, queryData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -212,3 +215,33 @@ func GetCmdGetContractStateRaw(cdc *codec.Codec) *cobra.Command {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetCmdGetContractStateSmart(cdc *codec.Codec) *cobra.Command {
|
||||
return &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(cmd *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)
|
||||
var queryData []byte
|
||||
res, _, err := cliCtx.QueryWithData(route, queryData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// todo: decode response
|
||||
fmt.Println(string(res))
|
||||
return 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,22 +133,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 {
|
||||
|
@ -152,13 +145,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)
|
||||
|
||||
|
@ -170,6 +160,42 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
return types.CosmosResult(*res), nil
|
||||
}
|
||||
|
||||
func (k Keeper) Query(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) (*wasmTypes.QueryResult, sdk.Error) {
|
||||
ctx = ctx.WithGasMeter(sdk.NewGasMeter(k.queryGasLimit))
|
||||
|
||||
codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, gasForContract(ctx))
|
||||
if qErr != nil {
|
||||
return nil, types.ErrExecuteFailed(qErr)
|
||||
}
|
||||
consumeGas(ctx, gasUsed)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -205,6 +231,12 @@ func (k Keeper) GetContractState(ctx sdk.Context, contractAddress sdk.AccAddress
|
|||
return prefixStore.Iterator(nil, nil)
|
||||
}
|
||||
|
||||
func (k Keeper) getContractStateForKey(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte {
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
return prefixStore.Get(key)
|
||||
}
|
||||
|
||||
func (k Keeper) setContractState(ctx sdk.Context, contractAddress sdk.AccAddress, models []types.Model) {
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
|
@ -21,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) {
|
||||
|
@ -30,18 +35,10 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||
case QueryListContracts:
|
||||
return queryContractList(ctx, req, keeper)
|
||||
case QueryGetContractState:
|
||||
var accept func([]byte) bool
|
||||
if len(path) > 2 { // passed with key
|
||||
binKey := []byte(path[2])
|
||||
accept = func(b []byte) bool {
|
||||
return bytes.Equal(b, binKey)
|
||||
}
|
||||
} else {
|
||||
accept = func(b []byte) bool {
|
||||
return true
|
||||
}
|
||||
if len(path) < 3 {
|
||||
return nil, sdk.ErrUnknownRequest("unknown data query endpoint")
|
||||
}
|
||||
return selectContractState(ctx, path[1], keeper, accept)
|
||||
return queryContractState(ctx, path[1], path[2], req, keeper)
|
||||
case QueryGetCode:
|
||||
return queryCode(ctx, path[1], req, keeper)
|
||||
case QueryListCode:
|
||||
|
@ -79,25 +76,39 @@ func queryContractList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([
|
|||
return bz, nil
|
||||
}
|
||||
|
||||
func selectContractState(ctx sdk.Context, bech string, keeper Keeper, accept func([]byte) bool) ([]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() {
|
||||
if !accept(iter.Key()) {
|
||||
continue
|
||||
var result interface{}
|
||||
switch queryMethod {
|
||||
case QueryMethodContractStateAll:
|
||||
var state []types.Model
|
||||
for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() {
|
||||
state = append(state, types.Model{
|
||||
Key: string(iter.Key()),
|
||||
Value: string(iter.Value()),
|
||||
})
|
||||
}
|
||||
m := types.Model{
|
||||
Key: string(iter.Key()),
|
||||
Value: string(iter.Value()),
|
||||
result = state
|
||||
case QueryMethodContractStateRaw:
|
||||
value := keeper.getContractStateForKey(ctx, contractAddr, req.Data)
|
||||
result = []types.Model{{
|
||||
Key: string(req.Data),
|
||||
Value: string(value),
|
||||
}}
|
||||
case QueryMethodContractStateSmart:
|
||||
res, err := keeper.Query(ctx, contractAddr, req.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state = append(state, m)
|
||||
result = res.Results
|
||||
default:
|
||||
return nil, sdk.ErrUnknownRequest("unsupported data query method for contract-state")
|
||||
}
|
||||
bz, err := json.MarshalIndent(state, "", " ")
|
||||
bz, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, sdk.ErrUnknownRequest(err.Error())
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -397,7 +398,7 @@ type model struct {
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue