228 lines
6.4 KiB
Go
228 lines
6.4 KiB
Go
package keeper
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
|
)
|
|
|
|
const (
|
|
QueryListContractByCode = "list-contracts-by-code"
|
|
QueryGetContract = "contract-info"
|
|
QueryGetContractState = "contract-state"
|
|
QueryGetCode = "code"
|
|
QueryListCode = "list-code"
|
|
)
|
|
|
|
const (
|
|
QueryMethodContractStateSmart = "smart"
|
|
QueryMethodContractStateAll = "all"
|
|
QueryMethodContractStateRaw = "raw"
|
|
)
|
|
|
|
// ContractInfoWithAddress adds the address (key) to the ContractInfo representation
|
|
type ContractInfoWithAddress struct {
|
|
// embedded here, so all json items remain top level
|
|
*types.ContractInfo
|
|
Address sdk.AccAddress `json:"address"`
|
|
}
|
|
|
|
// controls error output on querier - set true when testing/debugging
|
|
const debug = false
|
|
|
|
// NewQuerier creates a new querier
|
|
func NewQuerier(keeper Keeper) sdk.Querier {
|
|
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
|
|
switch path[0] {
|
|
case QueryGetContract:
|
|
return queryContractInfo(ctx, path[1], req, keeper)
|
|
case QueryListContractByCode:
|
|
return queryContractListByCode(ctx, path[1], req, keeper)
|
|
case QueryGetContractState:
|
|
if len(path) < 3 {
|
|
return nil, sdkerrors.Wrap(sdkerrors.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:
|
|
return queryCodeList(ctx, req, keeper)
|
|
default:
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown data query endpoint")
|
|
}
|
|
}
|
|
}
|
|
|
|
func queryContractInfo(ctx sdk.Context, bech string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
|
addr, err := sdk.AccAddressFromBech32(bech)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
|
|
}
|
|
info := keeper.GetContractInfo(ctx, addr)
|
|
if info == nil {
|
|
return []byte("null"), nil
|
|
}
|
|
// redact the Created field (just used for sorting, not part of public API)
|
|
info.Created = nil
|
|
|
|
infoWithAddress := ContractInfoWithAddress{
|
|
Address: addr,
|
|
ContractInfo: info,
|
|
}
|
|
bz, err := json.MarshalIndent(infoWithAddress, "", " ")
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
|
}
|
|
return bz, nil
|
|
}
|
|
|
|
func queryContractListByCode(ctx sdk.Context, codeIDstr string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
|
codeID, err := strconv.ParseUint(codeIDstr, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var contracts []ContractInfoWithAddress
|
|
keeper.ListContractInfo(ctx, func(addr sdk.AccAddress, info types.ContractInfo) bool {
|
|
if info.CodeID == codeID {
|
|
// remove init message on list
|
|
info.InitMsg = nil
|
|
// and add the address
|
|
infoWithAddress := ContractInfoWithAddress{
|
|
Address: addr,
|
|
ContractInfo: &info,
|
|
}
|
|
contracts = append(contracts, infoWithAddress)
|
|
}
|
|
return false
|
|
})
|
|
|
|
// now we sort them by CreatedAt
|
|
sort.Slice(contracts, func(i, j int) bool {
|
|
return contracts[i].ContractInfo.Created.LessThan(contracts[j].ContractInfo.Created)
|
|
})
|
|
// and remove that info for the final json (yes, the json:"-" tag doesn't work)
|
|
for i := range contracts {
|
|
contracts[i].Created = nil
|
|
}
|
|
|
|
bz, err := json.MarshalIndent(contracts, "", " ")
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
|
}
|
|
return bz, nil
|
|
}
|
|
|
|
func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
|
contractAddr, err := sdk.AccAddressFromBech32(bech)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, bech)
|
|
}
|
|
|
|
var resultData []types.Model
|
|
switch queryMethod {
|
|
case QueryMethodContractStateAll:
|
|
// this returns a serialized json object (which internally encoded binary fields properly)
|
|
for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() {
|
|
resultData = append(resultData, types.Model{
|
|
Key: iter.Key(),
|
|
Value: iter.Value(),
|
|
})
|
|
}
|
|
if resultData == nil {
|
|
resultData = make([]types.Model, 0)
|
|
}
|
|
case QueryMethodContractStateRaw:
|
|
// this returns a serialized json object
|
|
resultData = keeper.QueryRaw(ctx, contractAddr, req.Data)
|
|
case QueryMethodContractStateSmart:
|
|
// this returns raw bytes (must be base64-encoded)
|
|
return keeper.QuerySmart(ctx, contractAddr, req.Data)
|
|
default:
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, queryMethod)
|
|
}
|
|
bz, err := json.Marshal(resultData)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
|
}
|
|
return bz, nil
|
|
}
|
|
|
|
type GetCodeResponse struct {
|
|
ListCodeResponse
|
|
// Data is the entire wasm bytecode
|
|
Data []byte `json:"data" yaml:"data"`
|
|
}
|
|
|
|
func queryCode(ctx sdk.Context, codeIDstr string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
|
codeID, err := strconv.ParseUint(codeIDstr, 10, 64)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "invalid codeID: "+err.Error())
|
|
}
|
|
|
|
res := keeper.GetCodeInfo(ctx, codeID)
|
|
if res == nil {
|
|
// nil, nil leads to 404 in rest handler
|
|
return nil, nil
|
|
}
|
|
info := ListCodeResponse{
|
|
ID: codeID,
|
|
Creator: res.Creator,
|
|
DataHash: res.CodeHash,
|
|
Source: res.Source,
|
|
Builder: res.Builder,
|
|
}
|
|
|
|
code, err := keeper.GetByteCode(ctx, codeID)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "loading wasm code")
|
|
}
|
|
|
|
bz, err := json.MarshalIndent(GetCodeResponse{info, code}, "", " ")
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
|
}
|
|
return bz, nil
|
|
}
|
|
|
|
type ListCodeResponse struct {
|
|
ID uint64 `json:"id"`
|
|
Creator sdk.AccAddress `json:"creator"`
|
|
DataHash tmbytes.HexBytes `json:"data_hash"`
|
|
Source string `json:"source"`
|
|
Builder string `json:"builder"`
|
|
}
|
|
|
|
func queryCodeList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
|
var info []ListCodeResponse
|
|
|
|
var i uint64
|
|
for true {
|
|
i++
|
|
res := keeper.GetCodeInfo(ctx, i)
|
|
if res == nil {
|
|
break
|
|
}
|
|
info = append(info, ListCodeResponse{
|
|
ID: i,
|
|
Creator: res.Creator,
|
|
DataHash: res.CodeHash,
|
|
Source: res.Source,
|
|
Builder: res.Builder,
|
|
})
|
|
}
|
|
|
|
bz, err := json.MarshalIndent(info, "", " ")
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
|
}
|
|
return bz, nil
|
|
}
|