Added stubs for handler, querier to compile code

This commit is contained in:
Ethan Frey
2020-05-06 11:57:55 +02:00
parent 03ad1fb1a0
commit a53bfdf612
6 changed files with 191 additions and 142 deletions

View File

@@ -0,0 +1,147 @@
package keeper
import (
"encoding/json"
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
)
type MessageHandler struct {
router sdk.Router
encoders MessageEncoders
}
type MessageEncoders struct {
Bank func(msg *wasmTypes.BankMsg) (sdk.Msg, error)
Custom func(msg json.RawMessage) (sdk.Msg, error)
Staking func(msg *wasmTypes.StakingMsg) (sdk.Msg, error)
Wasm func(msg *wasmTypes.WasmMsg) (sdk.Msg, error)
}
func DefaultEncoders() MessageEncoders {
return MessageEncoders{
Bank: EncodeBankMsg,
Custom: NoCustomMsg,
Staking: EncodeStakingMsg,
Wasm: EncodeWasmMsg,
}
}
func EncodeBankMsg(msg *wasmTypes.BankMsg) (sdk.Msg, error) {
if msg.Send == nil {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Bank")
}
if len(msg.Send.Amount) == 0 {
return nil, nil
}
fromAddr, stderr := sdk.AccAddressFromBech32(msg.Send.FromAddress)
if stderr != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Send.FromAddress)
}
toAddr, stderr := sdk.AccAddressFromBech32(msg.Send.ToAddress)
if stderr != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Send.ToAddress)
}
toSend, err := convertWasmCoinToSdkCoin(msg.Send.Amount)
if err != nil {
return nil, err
}
sendMsg := bank.MsgSend{
FromAddress: fromAddr,
ToAddress: toAddr,
Amount: toSend,
}
return sendMsg, nil
}
func NoCustomMsg(msg json.RawMessage) (sdk.Msg, error) {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Custom variant not supported")
}
func EncodeStakingMsg(msg *wasmTypes.StakingMsg) (sdk.Msg, error) {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Staking variant not supported")
}
func EncodeWasmMsg(msg *wasmTypes.WasmMsg) (sdk.Msg, error) {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Wasm variant not supported")
// } else if msg.Contract != nil {
// targetAddr, stderr := sdk.AccAddressFromBech32(msg.Contract.ContractAddr)
// if stderr != nil {
// return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Contract.ContractAddr)
// }
// sentFunds, err := convertWasmCoinToSdkCoin(msg.Contract.Send)
// if err != nil {
// return err
// }
// // TODO: special case?
// _, err = k.Execute(ctx, targetAddr, contractAddr, msg.Contract.Msg, sentFunds)
// return err // may be nil
}
func (h MessageHandler) Dispatch(ctx sdk.Context, contract exported.Account, msg wasmTypes.CosmosMsg) error {
// maybe use this instead for the arg?
contractAddr := contract.GetAddress()
var sdkMsg sdk.Msg
var err error
switch {
case msg.Bank != nil:
sdkMsg, err = h.encoders.Bank(msg.Bank)
case msg.Custom != nil:
sdkMsg, err = h.encoders.Custom(msg.Custom)
case msg.Staking != nil:
sdkMsg, err = h.encoders.Staking(msg.Staking)
case msg.Wasm != nil:
sdkMsg, err = h.encoders.Wasm(msg.Wasm)
}
if err != nil {
return err
}
// (msg=nil, err=nil) is a no-op, ignore the message (eg. send with no tokens)
if sdkMsg == nil {
return nil
}
return h.handleSdkMessage(ctx, contractAddr, sdkMsg)
}
func (h MessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) error {
// make sure this account can send it
for _, acct := range msg.GetSigners() {
if !acct.Equals(contractAddr) {
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
}
}
// find the handler and execute it
handler := h.router.Route(ctx, msg.Route())
if handler == nil {
return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, msg.Route())
}
res, err := handler(ctx, msg)
if err != nil {
return err
}
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
ctx.EventManager().EmitEvents(res.Events)
return nil
}
func convertWasmCoinToSdkCoin(coins []wasmTypes.Coin) (sdk.Coins, error) {
var toSend sdk.Coins
for _, coin := range coins {
amount, ok := sdk.NewIntFromString(coin.Amount)
if !ok {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, coin.Amount+coin.Denom)
}
c := sdk.Coin{
Denom: coin.Denom,
Amount: amount,
}
toSend = append(toSend, c)
}
return toSend, nil
}

View File

@@ -2,7 +2,6 @@ package keeper
import (
"encoding/binary"
"fmt"
"path/filepath"
wasm "github.com/CosmWasm/go-cosmwasm"
@@ -37,7 +36,9 @@ type Keeper struct {
router sdk.Router
wasmer wasm.Wasmer
wasmer wasm.Wasmer
querier QueryHandler
messenger MessageHandler
// queryGasLimit is the max wasm gas that can be spent on executing a query with a contract
queryGasLimit uint64
}
@@ -50,13 +51,18 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou
panic(err)
}
messenger := MessageHandler{
router: router,
}
return Keeper{
storeKey: storeKey,
cdc: cdc,
wasmer: *wasmer,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
router: router,
messenger: messenger,
querier: QueryHandler{}, // TODO: not empty
queryGasLimit: wasmConfig.SmartQueryGasLimit,
}
}
@@ -127,7 +133,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre
// instantiate wasm contract
gas := gasForContract(ctx)
res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, gas)
res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, k.querier, gas)
if err != nil {
return contractAddress, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error())
// return contractAddress, sdkerrors.Wrap(err, "cosmwasm instantiate")
@@ -170,7 +176,7 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
params := types.NewParams(ctx, caller, coins, contractAccount)
gas := gasForContract(ctx)
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, gas)
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, k.querier, gas)
if execErr != nil {
// TODO: wasmer doesn't return gas used on error. we should consume it (for error on metering failure)
return sdk.Result{}, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
@@ -199,7 +205,7 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b
if err != nil {
return nil, err
}
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, gasForContract(ctx))
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, k.querier, gasForContract(ctx))
if qErr != nil {
return nil, sdkerrors.Wrap(types.ErrQueryFailed, qErr.Error())
}
@@ -313,112 +319,13 @@ func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) {
func (k Keeper) dispatchMessages(ctx sdk.Context, contract exported.Account, msgs []wasmTypes.CosmosMsg) error {
for _, msg := range msgs {
if err := k.dispatchMessage(ctx, contract, msg); err != nil {
if err := k.messenger.Dispatch(ctx, contract, msg); err != nil {
return err
}
}
return nil
}
func (k Keeper) dispatchMessage(ctx sdk.Context, contract exported.Account, msg wasmTypes.CosmosMsg) error {
// maybe use this instead for the arg?
contractAddr := contract.GetAddress()
if msg.Send != nil {
return k.sendTokens(ctx, contractAddr, msg.Send.FromAddress, msg.Send.ToAddress, msg.Send.Amount)
} else if msg.Contract != nil {
targetAddr, stderr := sdk.AccAddressFromBech32(msg.Contract.ContractAddr)
if stderr != nil {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Contract.ContractAddr)
}
sentFunds, err := convertWasmCoinToSdkCoin(msg.Contract.Send)
if err != nil {
return err
}
_, err = k.Execute(ctx, targetAddr, contractAddr, msg.Contract.Msg, sentFunds)
return err // may be nil
} else if msg.Opaque != nil {
msg, err := ParseOpaqueMsg(k.cdc, msg.Opaque)
if err != nil {
return err
}
return k.handleSdkMessage(ctx, contractAddr, msg)
}
// what is it?
panic(fmt.Sprintf("Unknown CosmosMsg: %#v", msg))
}
func (k Keeper) sendTokens(ctx sdk.Context, signer sdk.AccAddress, origin string, target string, tokens []wasmTypes.Coin) error {
if len(tokens) == 0 {
return nil
}
msg, err := convertCosmosSendMsg(origin, target, tokens)
if err != nil {
return err
}
return k.handleSdkMessage(ctx, signer, msg)
}
func convertCosmosSendMsg(from string, to string, coins []wasmTypes.Coin) (bank.MsgSend, error) {
fromAddr, stderr := sdk.AccAddressFromBech32(from)
if stderr != nil {
return bank.MsgSend{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, from)
}
toAddr, stderr := sdk.AccAddressFromBech32(to)
if stderr != nil {
return bank.MsgSend{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, to)
}
toSend, err := convertWasmCoinToSdkCoin(coins)
if err != nil {
return bank.MsgSend{}, err
}
sendMsg := bank.MsgSend{
FromAddress: fromAddr,
ToAddress: toAddr,
Amount: toSend,
}
return sendMsg, nil
}
func convertWasmCoinToSdkCoin(coins []wasmTypes.Coin) (sdk.Coins, error) {
var toSend sdk.Coins
for _, coin := range coins {
amount, ok := sdk.NewIntFromString(coin.Amount)
if !ok {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, coin.Amount+coin.Denom)
}
c := sdk.Coin{
Denom: coin.Denom,
Amount: amount,
}
toSend = append(toSend, c)
}
return toSend, nil
}
func (k Keeper) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) error {
// make sure this account can send it
for _, acct := range msg.GetSigners() {
if !acct.Equals(contractAddr) {
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
}
}
// find the handler and execute it
h := k.router.Route(ctx, msg.Route())
if h == nil {
return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, msg.Route())
}
res, err := h(ctx, msg)
if err != nil {
return err
}
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
ctx.EventManager().EmitEvents(res.Events)
return nil
}
func gasForContract(ctx sdk.Context) uint64 {
meter := ctx.GasMeter()
remaining := (meter.Limit() - meter.GasConsumed()) * GasMultiplier

View File

@@ -1,34 +0,0 @@
package keeper
import (
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// ToCosmosMsg encodes an sdk msg using amino json encoding.
// Then wraps it as an opaque message
func ToCosmosMsg(cdc *codec.Codec, msg sdk.Msg) (wasmTypes.CosmosMsg, error) {
opaqueBz, err := cdc.MarshalJSON(msg)
if err != nil {
return wasmTypes.CosmosMsg{}, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
res := wasmTypes.CosmosMsg{
Opaque: &wasmTypes.OpaqueMsg{
Data: opaqueBz,
},
}
return res, nil
}
// ParseOpaqueMsg decodes msg.Data to an sdk.Msg using amino json encoding.
func ParseOpaqueMsg(cdc *codec.Codec, msg *wasmTypes.OpaqueMsg) (sdk.Msg, error) {
// until more is changes, format is amino json encoding, wrapped base64
var sdkmsg sdk.Msg
err := cdc.UnmarshalJSON(msg.Data, &sdkmsg)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
return sdkmsg, nil
}

View File

@@ -0,0 +1,27 @@
package keeper
import (
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
)
// TODO: make this something besides failure
// Give it sub-queriers
type QueryHandler struct{}
var _ wasmTypes.Querier = QueryHandler{}
func (q QueryHandler) Query(request wasmTypes.QueryRequest) ([]byte, error) {
//if request.Bank != nil {
// return q.Bank.Query(request.Bank)
//}
//if request.Custom != nil {
// return q.Custom.Query(request.Custom)
//}
//if request.Staking != nil {
// return nil, wasmTypes.UnsupportedRequest{"staking"}
//}
//if request.Wasm != nil {
// return nil, wasmTypes.UnsupportedRequest{"wasm"}
//}
return nil, wasmTypes.Unknown{}
}

View File

@@ -31,4 +31,7 @@ var (
// ErrQueryFailed error for rust smart query contract failure
ErrQueryFailed = sdkErrors.Register(DefaultCodespace, 8, "query wasm contract failed")
// ErrInvalidMsg error when we cannot process the error returned from the contract
ErrInvalidMsg = sdkErrors.Register(DefaultCodespace, 9, "invalid CosmosMsg from the contract")
)

View File

@@ -103,12 +103,11 @@ func NewParams(ctx sdk.Context, creator sdk.AccAddress, deposit sdk.Coins, contr
ChainID: ctx.ChainID(),
},
Message: wasmTypes.MessageInfo{
Signer: wasmTypes.CanonicalAddress(creator),
Sender: wasmTypes.CanonicalAddress(creator),
SentFunds: NewWasmCoins(deposit),
},
Contract: wasmTypes.ContractInfo{
Address: wasmTypes.CanonicalAddress(contractAcct.GetAddress()),
Balance: NewWasmCoins(contractAcct.GetCoins()),
},
}
}