Submsg and replies (#441)

* Add dispatchSubmessages and reply to Keeper

* Update all mock types

* Dispatch submessages in all entry points

* Rename mask -> reflect in all tests (that was cosmwasm 0.8...)

* Basic submessage dispatch test;

* Simplify messanger interface again

* Start table tests

* Added table tests

* Debuging handling out of gas and panics

* Properly handle gas limits and out of gas panics

* Test parsing return values from WasmMsg::Instantiate

* PR feedback

* Add test to trigger 0 len data panic

* Safely handle 0 sdk msg submsg responses

* Charge gas on reply
This commit is contained in:
Ethan Frey
2021-03-10 09:46:12 +01:00
committed by GitHub
parent fd81a6679a
commit 78d5581040
10 changed files with 839 additions and 191 deletions

View File

@@ -320,50 +320,49 @@ func convertWasmIBCTimeoutTimestampToCosmosTimestamp(timestamp *uint64) uint64 {
return *timestamp
}
func (h DefaultMessageHandler) Dispatch(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
for _, msg := range msgs {
sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg)
if err != nil {
return err
}
for _, sdkMsg := range sdkMsgs {
if err := h.handleSdkMessage(ctx, contractAddr, sdkMsg); err != nil {
return err
}
}
func (h DefaultMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg)
if err != nil {
return nil, nil, err
}
return nil
for _, sdkMsg := range sdkMsgs {
res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg)
if err != nil {
return nil, nil, err
}
// append data
data = append(data, res.Data)
// append events
sdkEvents := make([]sdk.Event, len(res.Events))
for i := range res.Events {
sdkEvents[i] = sdk.Event(res.Events[i])
}
events = append(events, sdkEvents...)
}
return
}
func (h DefaultMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) error {
func (h DefaultMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) {
if err := msg.ValidateBasic(); err != nil {
return err
return nil, err
}
// 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")
return nil, 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())
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, msg.Route())
}
res, err := handler(ctx, msg)
if err != nil {
return err
return nil, err
}
events := make(sdk.Events, len(res.Events))
for i := range res.Events {
events[i] = sdk.Event(res.Events[i])
}
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
ctx.EventManager().EmitEvents(events)
return nil
return res, nil
}
func convertWasmCoinsToSdkCoins(coins []wasmvmtypes.Coin) (sdk.Coins, error) {

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
"path/filepath"
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
@@ -52,7 +53,7 @@ type Option interface {
}
type messenger interface {
Dispatch(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error
DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error)
}
// Keeper will have a reference to Wasmer with it's own data directory.
@@ -220,7 +221,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authZ AuthorizationPolicy) (sdk.AccAddress, []byte, error) {
if !k.IsPinnedCode(ctx, codeID) {
ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: init")
ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: instantiate")
}
// create contract address
@@ -301,13 +302,14 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
contractInfo.IBCPortID = ibcPort
}
// store contract before dispatch so that contract could be called back
k.storeContractInfo(ctx, contractAddress, &contractInfo)
k.appendToContractHistory(ctx, contractAddress, contractInfo.InitialHistory(initMsg))
// then dispatch so that contract could be called back
err = k.dispatchMessages(ctx, contractAddress, contractInfo.IBCPortID, res.Messages)
// dispatch submessages then messages
err = k.dispatchAll(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages)
if err != nil {
return nil, nil, err
return nil, nil, sdkerrors.Wrap(err, "dispatch")
}
return contractAddress, res.Data, nil
@@ -352,9 +354,10 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
events := types.ParseEvents(res.Attributes, contractAddress)
ctx.EventManager().EmitEvents(events)
err = k.dispatchMessages(ctx, contractAddress, contractInfo.IBCPortID, res.Messages)
// dispatch submessages then messages
err = k.dispatchAll(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages)
if err != nil {
return nil, err
return nil, sdkerrors.Wrap(err, "dispatch")
}
return &sdk.Result{
@@ -426,8 +429,9 @@ func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller
k.appendToContractHistory(ctx, contractAddress, historyEntry)
k.storeContractInfo(ctx, contractAddress, contractInfo)
// then dispatch
if err := k.dispatchMessages(ctx, contractAddress, contractInfo.IBCPortID, res.Messages); err != nil {
// dispatch submessages then messages
err = k.dispatchAll(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages)
if err != nil {
return nil, sdkerrors.Wrap(err, "dispatch")
}
@@ -464,7 +468,50 @@ func (k Keeper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte
events := types.ParseEvents(res.Attributes, contractAddress)
ctx.EventManager().EmitEvents(events)
err = k.dispatchMessages(ctx, contractAddress, contractInfo.IBCPortID, res.Messages)
// dispatch submessages then messages
err = k.dispatchAll(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages)
if err != nil {
return nil, sdkerrors.Wrap(err, "dispatch")
}
return &sdk.Result{
Data: res.Data,
}, nil
}
// reply is only called from keeper internal functions (dispatchSubmessages) after processing the submessage
// it
func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply wasmvmtypes.Reply) (*sdk.Result, error) {
contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress)
if err != nil {
return nil, err
}
// current thought is to charge gas like a fresh run, we can revisit whether to give it a discount later
if !k.IsPinnedCode(ctx, contractInfo.CodeID) {
ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: reply")
}
env := types.NewEnv(ctx, contractAddress)
// prepare querier
querier := QueryHandler{
Ctx: ctx,
Plugins: k.queryPlugins,
}
gas := gasForContract(ctx)
res, gasUsed, execErr := k.wasmer.Reply(codeInfo.CodeHash, env, reply, prefixStore, cosmwasmAPI, querier, gasMeter(ctx), gas)
consumeGas(ctx, gasUsed)
if execErr != nil {
return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
}
// emit all events from this contract itself
events := types.ParseEvents(res.Attributes, contractAddress)
ctx.EventManager().EmitEvents(events)
// dispatch submessages then messages
err = k.dispatchAll(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages)
if err != nil {
return nil, sdkerrors.Wrap(err, "dispatch")
}
@@ -678,15 +725,6 @@ func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) {
return k.wasmer.GetCode(codeInfo.CodeHash)
}
func (k Keeper) dispatchMessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.CosmosMsg) error {
for _, msg := range msgs {
if err := k.messenger.Dispatch(ctx, contractAddr, ibcPort, msg); err != nil {
return err
}
}
return nil
}
// PinCode pins the wasm contract in wasmvm cache
func (k Keeper) PinCode(ctx sdk.Context, codeID uint64) error {
codeInfo := k.GetCodeInfo(ctx, codeID)
@@ -740,6 +778,139 @@ func (k Keeper) InitializePinnedCodes(ctx sdk.Context) error {
return nil
}
func (k Keeper) dispatchAll(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, subMsgs []wasmvmtypes.SubMsg, msgs []wasmvmtypes.CosmosMsg) error {
// first dispatch all submessages (and the replies).
err := k.dispatchSubmessages(ctx, contractAddr, ibcPort, subMsgs)
if err != nil {
return err
}
// then dispatch all the normal messages
return k.dispatchMessages(ctx, contractAddr, ibcPort, msgs)
}
func (k Keeper) dispatchMessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.CosmosMsg) error {
for _, msg := range msgs {
events, _, err := k.messenger.DispatchMsg(ctx, contractAddr, ibcPort, msg)
if err != nil {
return err
}
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
ctx.EventManager().EmitEvents(events)
}
return nil
}
func (k Keeper) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msg wasmvmtypes.CosmosMsg, gasLimit uint64) (events []sdk.Event, data [][]byte, err error) {
limitedMeter := sdk.NewGasMeter(gasLimit)
subCtx := ctx.WithGasMeter(limitedMeter)
// catch out of gas panic and just charge the entire gas limit
defer func() {
if r := recover(); r != nil {
// if it's not an OutOfGas error, raise it again
if _, ok := r.(sdk.ErrorOutOfGas); !ok {
// log it to get the original stack trace somewhere (as panic(r) keeps message but stacktrace to here
k.Logger(ctx).Info("SubMsg rethrowing panic: %#v", r)
panic(r)
}
ctx.GasMeter().ConsumeGas(gasLimit, "Sub-Message OutOfGas panic")
err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "SubMsg hit gas limit")
}
}()
events, data, err = k.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg)
// make sure we charge the parent what was spent
spent := subCtx.GasMeter().GasConsumed()
ctx.GasMeter().ConsumeGas(spent, "From limited Sub-Message")
return events, data, err
}
// dispatchSubmessages builds a sandbox to execute these messages and returns the execution result to the contract
// that dispatched them, both on success as well as failure
func (k Keeper) dispatchSubmessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) error {
for _, msg := range msgs {
// first, we build a sub-context which we can use inside the submessages
subCtx, commit := ctx.CacheContext()
// check how much gas left locally, optionally wrap the gas meter
gasRemaining := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumed()
limitGas := msg.GasLimit != nil && (*msg.GasLimit < gasRemaining)
var err error
var events []sdk.Event
var data [][]byte
if limitGas {
events, data, err = k.dispatchMsgWithGasLimit(subCtx, contractAddr, ibcPort, msg.Msg, *msg.GasLimit)
} else {
events, data, err = k.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg.Msg)
}
// if it succeeds, commit state changes from submessage, and pass on events to Event Manager
if err == nil {
commit()
ctx.EventManager().EmitEvents(events)
}
// on failure, revert state from sandbox, and ignore events (just skip doing the above)
var result wasmvmtypes.SubcallResult
if err == nil {
// just take the first one for now if there are multiple sub-sdk messages
// and safely return nothing if no data
var responseData []byte
if len(data) > 0 {
responseData = data[0]
}
result = wasmvmtypes.SubcallResult{
Ok: &wasmvmtypes.SubcallResponse{
Events: sdkEventsToWasmVmEvents(events),
Data: responseData,
},
}
} else {
result = wasmvmtypes.SubcallResult{
Err: err.Error(),
}
}
// now handle the reply, we use the parent context, and abort on error
reply := wasmvmtypes.Reply{
ID: msg.ID,
Result: result,
}
// we can ignore any result returned as there is nothing to do with the data
// and the events are already in the ctx.EventManager()
_, err = k.reply(ctx, contractAddr, reply)
if err != nil {
return err
}
}
return nil
}
func sdkEventsToWasmVmEvents(events []sdk.Event) []wasmvmtypes.Event {
res := make([]wasmvmtypes.Event, len(events))
for i, ev := range events {
res[i] = wasmvmtypes.Event{
Type: ev.Type,
Attributes: sdkAttributesToWasmVmAttributes(ev.Attributes),
}
}
return res
}
func sdkAttributesToWasmVmAttributes(attrs []abci.EventAttribute) []wasmvmtypes.EventAttribute {
res := make([]wasmvmtypes.EventAttribute, len(attrs))
for i, attr := range attrs {
res[i] = wasmvmtypes.EventAttribute{
Key: string(attr.Key),
Value: string(attr.Value),
}
}
return res
}
func gasForContract(ctx sdk.Context) uint64 {
meter := ctx.GasMeter()
if meter.IsOutOfGas() {

View File

@@ -20,12 +20,13 @@ import (
"github.com/stretchr/testify/require"
)
// MaskInitMsg is {}
// ReflectInitMsg is {}
// MaskHandleMsg is used to encode handle messages
type MaskHandleMsg struct {
Reflect *reflectPayload `json:"reflect_msg,omitempty"`
Change *ownerPayload `json:"change_owner,omitempty"`
// ReflectHandleMsg is used to encode handle messages
type ReflectHandleMsg struct {
Reflect *reflectPayload `json:"reflect_msg,omitempty"`
ReflectSubCall *reflectSubPayload `json:"reflect_sub_call,omitempty"`
Change *ownerPayload `json:"change_owner,omitempty"`
}
type ownerPayload struct {
@@ -36,11 +37,16 @@ type reflectPayload struct {
Msgs []wasmvmtypes.CosmosMsg `json:"msgs"`
}
// MaskQueryMsg is used to encode query messages
type MaskQueryMsg struct {
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *ChainQuery `json:"chain,omitempty"`
type reflectSubPayload struct {
Msgs []wasmvmtypes.SubMsg `json:"msgs"`
}
// ReflectQueryMsg is used to encode query messages
type ReflectQueryMsg struct {
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *ChainQuery `json:"chain,omitempty"`
SubCallResult *SubCall `json:"sub_call_result,omitempty"`
}
type ChainQuery struct {
@@ -51,6 +57,10 @@ type Text struct {
Text string `json:"text"`
}
type SubCall struct {
ID uint64 `json:"id"`
}
type OwnerResponse struct {
Owner string `json:"owner,omitempty"`
}
@@ -59,7 +69,7 @@ type ChainResponse struct {
Data []byte `json:"data,omitempty"`
}
func buildMaskQuery(t *testing.T, query *MaskQueryMsg) []byte {
func buildReflectQuery(t *testing.T, query *ReflectQueryMsg) []byte {
bz, err := json.Marshal(query)
require.NoError(t, err)
return bz
@@ -70,23 +80,23 @@ func mustParse(t *testing.T, data []byte, res interface{}) {
require.NoError(t, err)
}
const MaskFeatures = "staking,mask,stargate"
const ReflectFeatures = "staking,mask,stargate"
func TestMaskReflectContractSend(t *testing.T) {
func TestReflectContractSend(t *testing.T) {
cdc := MakeTestCodec(t)
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), nil)
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(cdc), nil)
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, deposit)
_, _, bob := keyPubAddr()
// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
reflectID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), maskID)
require.Equal(t, uint64(1), reflectID)
// upload hackatom escrow code
escrowCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
@@ -96,14 +106,14 @@ func TestMaskReflectContractSend(t *testing.T) {
require.Equal(t, uint64(2), escrowID)
// creator instantiates a contract and gives it tokens
maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
maskAddr, _, err := keeper.Instantiate(ctx, maskID, creator, nil, []byte("{}"), "mask contract 2", maskStart)
reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
reflectAddr, _, err := keeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
require.NoError(t, err)
require.NotEmpty(t, maskAddr)
require.NotEmpty(t, reflectAddr)
// now we set contract as verifier of an escrow
initMsg := HackatomExampleInitMsg{
Verifier: maskAddr,
Verifier: reflectAddr,
Beneficiary: bob,
}
initMsgBz, err := json.Marshal(initMsg)
@@ -115,13 +125,13 @@ func TestMaskReflectContractSend(t *testing.T) {
// let's make sure all balances make sense
checkAccount(t, ctx, accKeeper, bankKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // 100k - 40k - 25k
checkAccount(t, ctx, accKeeper, bankKeeper, maskAddr, maskStart)
checkAccount(t, ctx, accKeeper, bankKeeper, reflectAddr, reflectStart)
checkAccount(t, ctx, accKeeper, bankKeeper, escrowAddr, escrowStart)
checkAccount(t, ctx, accKeeper, bankKeeper, bob, nil)
// now for the trick.... we reflect a message through the mask to call the escrow
// now for the trick.... we reflect a message through the reflect to call the escrow
// we also send an additional 14k tokens there.
// this should reduce the mask balance by 14k (to 26k)
// this should reduce the reflect balance by 14k (to 26k)
// this 14k is added to the escrow, then the entire balance is sent to bob (total: 39k)
approveMsg := []byte(`{"release":{}}`)
msgs := []wasmvmtypes.CosmosMsg{{
@@ -136,27 +146,27 @@ func TestMaskReflectContractSend(t *testing.T) {
},
},
}}
reflectSend := MaskHandleMsg{
reflectSend := ReflectHandleMsg{
Reflect: &reflectPayload{
Msgs: msgs,
},
}
reflectSendBz, err := json.Marshal(reflectSend)
require.NoError(t, err)
_, err = keeper.Execute(ctx, maskAddr, creator, reflectSendBz, nil)
_, err = keeper.Execute(ctx, reflectAddr, creator, reflectSendBz, nil)
require.NoError(t, err)
// did this work???
checkAccount(t, ctx, accKeeper, bankKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // same as before
checkAccount(t, ctx, accKeeper, bankKeeper, maskAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 26000))) // 40k - 14k (from send)
checkAccount(t, ctx, accKeeper, bankKeeper, escrowAddr, sdk.Coins{}) // emptied reserved
checkAccount(t, ctx, accKeeper, bankKeeper, bob, sdk.NewCoins(sdk.NewInt64Coin("denom", 39000))) // all escrow of 25k + 14k
checkAccount(t, ctx, accKeeper, bankKeeper, creator, sdk.NewCoins(sdk.NewInt64Coin("denom", 35000))) // same as before
checkAccount(t, ctx, accKeeper, bankKeeper, reflectAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 26000))) // 40k - 14k (from send)
checkAccount(t, ctx, accKeeper, bankKeeper, escrowAddr, sdk.Coins{}) // emptied reserved
checkAccount(t, ctx, accKeeper, bankKeeper, bob, sdk.NewCoins(sdk.NewInt64Coin("denom", 39000))) // all escrow of 25k + 14k
}
func TestMaskReflectCustomMsg(t *testing.T) {
func TestReflectCustomMsg(t *testing.T) {
cdc := MakeTestCodec(t)
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins())
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(cdc), reflectPlugins())
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
@@ -165,20 +175,20 @@ func TestMaskReflectCustomMsg(t *testing.T) {
_, _, fred := keyPubAddr()
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
codeID, err := keeper.Create(ctx, creator, reflectCode, "", "", 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 := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "mask contract 1", contractStart)
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
require.NoError(t, err)
require.NotEmpty(t, contractAddr)
// set owner to bob
transfer := MaskHandleMsg{
transfer := ReflectHandleMsg{
Change: &ownerPayload{
Owner: bob,
},
@@ -205,7 +215,7 @@ func TestMaskReflectCustomMsg(t *testing.T) {
},
},
}}
reflectSend := MaskHandleMsg{
reflectSend := ReflectHandleMsg{
Reflect: &reflectPayload{
Msgs: msgs,
},
@@ -227,9 +237,9 @@ func TestMaskReflectCustomMsg(t *testing.T) {
ToAddress: fred.String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("denom", 23000)),
}
opaque, err := toMaskRawMsg(cdc, sdkSendMsg)
opaque, err := toReflectRawMsg(cdc, sdkSendMsg)
require.NoError(t, err)
reflectOpaque := MaskHandleMsg{
reflectOpaque := ReflectHandleMsg{
Reflect: &reflectPayload{
Msgs: []wasmvmtypes.CosmosMsg{opaque},
},
@@ -249,27 +259,27 @@ func TestMaskReflectCustomMsg(t *testing.T) {
func TestMaskReflectCustomQuery(t *testing.T) {
cdc := MakeTestCodec(t)
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins())
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(cdc), reflectPlugins())
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, deposit)
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
codeID, err := keeper.Create(ctx, creator, reflectCode, "", "", 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 := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "mask contract 1", contractStart)
contractAddr, _, err := keeper.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 := MaskQueryMsg{
ownerQuery := ReflectQueryMsg{
Owner: &struct{}{},
}
ownerQueryBz, err := json.Marshal(ownerQuery)
@@ -282,7 +292,7 @@ func TestMaskReflectCustomQuery(t *testing.T) {
assert.Equal(t, res.Owner, creator.String())
// and now making use of the custom querier callbacks
customQuery := MaskQueryMsg{
customQuery := ReflectQueryMsg{
Capitalized: &Text{
Text: "all Caps noW",
},
@@ -299,7 +309,7 @@ func TestMaskReflectCustomQuery(t *testing.T) {
func TestReflectStargateQuery(t *testing.T) {
cdc := MakeTestCodec(t)
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins())
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(cdc), reflectPlugins())
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
@@ -308,14 +318,14 @@ func TestReflectStargateQuery(t *testing.T) {
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, funds)
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
codeID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
// creator instantiates a contract and gives it tokens
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "mask contract 1", contractStart)
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
require.NoError(t, err)
require.NotEmpty(t, contractAddr)
@@ -327,7 +337,7 @@ func TestReflectStargateQuery(t *testing.T) {
},
},
}
simpleQueryBz, err := json.Marshal(MaskQueryMsg{
simpleQueryBz, err := json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &bankQuery},
})
require.NoError(t, err)
@@ -352,7 +362,7 @@ func TestReflectStargateQuery(t *testing.T) {
Data: protoQueryBin,
},
}
protoQueryBz, err := json.Marshal(MaskQueryMsg{
protoQueryBz, err := json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &protoRequest},
})
require.NoError(t, err)
@@ -370,33 +380,33 @@ func TestReflectStargateQuery(t *testing.T) {
assert.Equal(t, expectedBalance, protoResult.Balances)
}
type maskState struct {
type reflectState struct {
Owner []byte `json:"owner"`
}
func TestMaskReflectWasmQueries(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec(t)), nil)
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(MakeTestCodec(t)), nil)
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(t, ctx, accKeeper, keepers.BankKeeper, deposit)
// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
reflectID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), maskID)
require.Equal(t, uint64(1), reflectID)
// creator instantiates a contract and gives it tokens
maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
maskAddr, _, err := keeper.Instantiate(ctx, maskID, creator, nil, []byte("{}"), "mask contract 2", maskStart)
reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
reflectAddr, _, err := keeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
require.NoError(t, err)
require.NotEmpty(t, maskAddr)
require.NotEmpty(t, reflectAddr)
// for control, let's make some queries directly on the mask
ownerQuery := buildMaskQuery(t, &MaskQueryMsg{Owner: &struct{}{}})
res, err := keeper.QuerySmart(ctx, maskAddr, ownerQuery)
// for control, let's make some queries directly on the reflect
ownerQuery := buildReflectQuery(t, &ReflectQueryMsg{Owner: &struct{}{}})
res, err := keeper.QuerySmart(ctx, reflectAddr, ownerQuery)
require.NoError(t, err)
var ownerRes OwnerResponse
mustParse(t, res, &ownerRes)
@@ -404,20 +414,20 @@ func TestMaskReflectWasmQueries(t *testing.T) {
// 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, maskAddr, configKey)
var stateRes maskState
raw := keeper.QueryRaw(ctx, reflectAddr, configKey)
var stateRes reflectState
mustParse(t, raw, &stateRes)
require.Equal(t, stateRes.Owner, []byte(creator))
// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
reflectOwnerQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectOwnerQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Smart: &wasmvmtypes.SmartQuery{
ContractAddr: maskAddr.String(),
ContractAddr: reflectAddr.String(),
Msg: ownerQuery,
},
}}}}
reflectOwnerBin := buildMaskQuery(t, &reflectOwnerQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectOwnerBin)
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 ChainResponse
@@ -427,58 +437,58 @@ func TestMaskReflectWasmQueries(t *testing.T) {
require.Equal(t, reflectOwnerRes.Owner, creator.String())
// and with queryRaw
reflectStateQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectStateQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Raw: &wasmvmtypes.RawQuery{
ContractAddr: maskAddr.String(),
ContractAddr: reflectAddr.String(),
Key: configKey,
},
}}}}
reflectStateBin := buildMaskQuery(t, &reflectStateQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectStateBin)
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 ChainResponse
mustParse(t, res, &reflectRawRes)
// now, with the raw data, we can parse it into state
var reflectStateRes maskState
var reflectStateRes reflectState
mustParse(t, reflectRawRes.Data, &reflectStateRes)
require.Equal(t, reflectStateRes.Owner, []byte(creator))
}
func TestWasmRawQueryWithNil(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec(t)), nil)
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, reflectEncoders(MakeTestCodec(t)), nil)
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(t, ctx, accKeeper, keepers.BankKeeper, deposit)
// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
reflectID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), maskID)
require.Equal(t, uint64(1), reflectID)
// creator instantiates a contract and gives it tokens
maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
maskAddr, _, err := keeper.Instantiate(ctx, maskID, creator, nil, []byte("{}"), "mask contract 2", maskStart)
reflectStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
reflectAddr, _, err := keeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), "reflect contract 2", reflectStart)
require.NoError(t, err)
require.NotEmpty(t, maskAddr)
require.NotEmpty(t, reflectAddr)
// control: query directly
missingKey := []byte{0, 1, 2, 3, 4}
raw := keeper.QueryRaw(ctx, maskAddr, missingKey)
raw := keeper.QueryRaw(ctx, reflectAddr, missingKey)
require.Nil(t, raw)
// and with queryRaw
reflectQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Raw: &wasmvmtypes.RawQuery{
ContractAddr: maskAddr.String(),
ContractAddr: reflectAddr.String(),
Key: missingKey,
},
}}}}
reflectStateBin := buildMaskQuery(t, &reflectQuery)
res, err := keeper.QuerySmart(ctx, maskAddr, reflectStateBin)
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
@@ -507,14 +517,14 @@ func checkAccount(t *testing.T, ctx sdk.Context, accKeeper authkeeper.AccountKee
/**** Code to support custom messages *****/
type maskCustomMsg struct {
type reflectCustomMsg struct {
Debug string `json:"debug,omitempty"`
Raw []byte `json:"raw,omitempty"`
}
// toMaskRawMsg encodes an sdk msg using any type with json encoding.
// toReflectRawMsg encodes an sdk msg using any type with json encoding.
// Then wraps it as an opaque message
func toMaskRawMsg(cdc codec.Marshaler, msg sdk.Msg) (wasmvmtypes.CosmosMsg, error) {
func toReflectRawMsg(cdc codec.Marshaler, msg sdk.Msg) (wasmvmtypes.CosmosMsg, error) {
any, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return wasmvmtypes.CosmosMsg{}, err
@@ -523,7 +533,7 @@ func toMaskRawMsg(cdc codec.Marshaler, msg sdk.Msg) (wasmvmtypes.CosmosMsg, erro
if err != nil {
return wasmvmtypes.CosmosMsg{}, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
customMsg, err := json.Marshal(maskCustomMsg{
customMsg, err := json.Marshal(reflectCustomMsg{
Raw: rawBz,
})
res := wasmvmtypes.CosmosMsg{
@@ -532,18 +542,18 @@ func toMaskRawMsg(cdc codec.Marshaler, msg sdk.Msg) (wasmvmtypes.CosmosMsg, erro
return res, nil
}
// maskEncoders needs to be registered in test setup to handle custom message callbacks
func maskEncoders(cdc codec.Marshaler) *MessageEncoders {
// reflectEncoders needs to be registered in test setup to handle custom message callbacks
func reflectEncoders(cdc codec.Marshaler) *MessageEncoders {
return &MessageEncoders{
Custom: fromMaskRawMsg(cdc),
Custom: fromReflectRawMsg(cdc),
}
}
// fromMaskRawMsg decodes msg.Data to an sdk.Msg using proto Any and json encoding.
// fromReflectRawMsg decodes msg.Data to an sdk.Msg using proto Any and json encoding.
// this needs to be registered on the Encoders
func fromMaskRawMsg(cdc codec.Marshaler) CustomEncoder {
func fromReflectRawMsg(cdc codec.Marshaler) CustomEncoder {
return func(_sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error) {
var custom maskCustomMsg
var custom reflectCustomMsg
err := json.Unmarshal(msg, &custom)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
@@ -566,7 +576,7 @@ func fromMaskRawMsg(cdc codec.Marshaler) CustomEncoder {
}
}
type maskCustomQuery struct {
type reflectCustomQuery struct {
Ping *struct{} `json:"ping,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
}
@@ -589,15 +599,15 @@ type chainResponse struct {
Data []byte `json:"data"`
}
// maskPlugins needs to be registered in test setup to handle custom query callbacks
func maskPlugins() *QueryPlugins {
// reflectPlugins needs to be registered in test setup to handle custom query callbacks
func reflectPlugins() *QueryPlugins {
return &QueryPlugins{
Custom: performCustomQuery,
}
}
func performCustomQuery(_ sdk.Context, request json.RawMessage) ([]byte, error) {
var custom maskCustomQuery
var custom reflectCustomQuery
err := json.Unmarshal(request, &custom)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())

View File

@@ -66,7 +66,8 @@ func (k Keeper) OnConnectChannel(
events := types.ParseEvents(res.Attributes, contractAddr)
ctx.EventManager().EmitEvents(events)
if err := k.messenger.Dispatch(ctx, contractAddr, contractInfo.IBCPortID, res.Messages...); err != nil {
// TODO: add submessages support here (and everywhere else) once https://github.com/CosmWasm/cosmwasm/issues/822 is merged
if err := k.dispatchAll(ctx, contractAddr, contractInfo.IBCPortID, nil, res.Messages); err != nil {
return err
}
return nil
@@ -102,7 +103,8 @@ func (k Keeper) OnCloseChannel(
events := types.ParseEvents(res.Attributes, contractAddr)
ctx.EventManager().EmitEvents(events)
if err := k.messenger.Dispatch(ctx, contractAddr, contractInfo.IBCPortID, res.Messages...); err != nil {
// TODO: add submessages support here (and everywhere else) once https://github.com/CosmWasm/cosmwasm/issues/822 is merged
if err := k.dispatchAll(ctx, contractAddr, contractInfo.IBCPortID, nil, res.Messages); err != nil {
return err
}
return nil
@@ -138,7 +140,8 @@ func (k Keeper) OnRecvPacket(
events := types.ParseEvents(res.Attributes, contractAddr)
ctx.EventManager().EmitEvents(events)
if err := k.messenger.Dispatch(ctx, contractAddr, contractInfo.IBCPortID, res.Messages...); err != nil {
// TODO: add submessages support here (and everywhere else) once https://github.com/CosmWasm/cosmwasm/issues/822 is merged
if err := k.dispatchAll(ctx, contractAddr, contractInfo.IBCPortID, nil, res.Messages); err != nil {
return nil, err
}
return res.Acknowledgement, nil
@@ -175,7 +178,8 @@ func (k Keeper) OnAckPacket(
events := types.ParseEvents(res.Attributes, contractAddr)
ctx.EventManager().EmitEvents(events)
if err := k.messenger.Dispatch(ctx, contractAddr, contractInfo.IBCPortID, res.Messages...); err != nil {
// TODO: add submessages support here (and everywhere else) once https://github.com/CosmWasm/cosmwasm/issues/822 is merged
if err := k.dispatchAll(ctx, contractAddr, contractInfo.IBCPortID, nil, res.Messages); err != nil {
return err
}
return nil
@@ -208,7 +212,8 @@ func (k Keeper) OnTimeoutPacket(
events := types.ParseEvents(res.Attributes, contractAddr)
ctx.EventManager().EmitEvents(events)
if err := k.messenger.Dispatch(ctx, contractAddr, contractInfo.IBCPortID, res.Messages...); err != nil {
// TODO: add submessages support here (and everywhere else) once https://github.com/CosmWasm/cosmwasm/issues/822 is merged
if err := k.dispatchAll(ctx, contractAddr, contractInfo.IBCPortID, nil, res.Messages); err != nil {
return err
}
return nil

View File

@@ -125,11 +125,7 @@ func TestOnConnectChannel(t *testing.T) {
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
},
overwriteMessenger: &wasmtesting.MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return errors.New("test, ignore")
},
},
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
expErr: true,
expContractEventAttrs: 1,
},
@@ -239,11 +235,7 @@ func TestOnCloseChannel(t *testing.T) {
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
},
overwriteMessenger: &wasmtesting.MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return errors.New("test, ignore")
},
},
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
expErr: true,
expContractEventAttrs: 1,
},
@@ -364,11 +356,7 @@ func TestOnRecvPacket(t *testing.T) {
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
},
overwriteMessenger: &wasmtesting.MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return errors.New("test, ignore")
},
},
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
expErr: true,
expContractEventAttrs: 1,
},
@@ -482,11 +470,7 @@ func TestOnAckPacket(t *testing.T) {
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
},
overwriteMessenger: &wasmtesting.MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return errors.New("test, ignore")
},
},
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
expErr: true,
expContractEventAttrs: 1,
},
@@ -597,11 +581,7 @@ func TestOnTimeoutPacket(t *testing.T) {
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
},
overwriteMessenger: &wasmtesting.MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return errors.New("test, ignore")
},
},
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
expErr: true,
expContractEventAttrs: 1,
},

View File

@@ -453,10 +453,10 @@ func TestQueryStakingInfo(t *testing.T) {
// STEP 3: now, let's reflect some queries.
// let's get the bonded denom
reflectBondedQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectBondedQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
BondedDenom: &struct{}{},
}}}}
reflectBondedBin := buildMaskQuery(t, &reflectBondedQuery)
reflectBondedBin := buildReflectQuery(t, &reflectBondedQuery)
res, err := keeper.QuerySmart(ctx, maskAddr, reflectBondedBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
@@ -467,10 +467,10 @@ func TestQueryStakingInfo(t *testing.T) {
assert.Equal(t, "stake", bondedRes.Denom)
// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
reflectValidatorsQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectValidatorsQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
Validators: &wasmvmtypes.ValidatorsQuery{},
}}}}
reflectValidatorsBin := buildMaskQuery(t, &reflectValidatorsQuery)
reflectValidatorsBin := buildReflectQuery(t, &reflectValidatorsQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectValidatorsBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
@@ -486,12 +486,12 @@ func TestQueryStakingInfo(t *testing.T) {
require.Contains(t, valInfo.MaxChangeRate, "0.010")
// test to get all my delegations
reflectAllDelegationsQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectAllDelegationsQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
AllDelegations: &wasmvmtypes.AllDelegationsQuery{
Delegator: contractAddr.String(),
},
}}}}
reflectAllDelegationsBin := buildMaskQuery(t, &reflectAllDelegationsQuery)
reflectAllDelegationsBin := buildReflectQuery(t, &reflectAllDelegationsQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectAllDelegationsBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
@@ -509,13 +509,13 @@ func TestQueryStakingInfo(t *testing.T) {
require.Equal(t, funds[0].Amount.String(), delInfo.Amount.Amount)
// test to get one delegations
reflectDelegationQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectDelegationQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
Delegation: &wasmvmtypes.DelegationQuery{
Validator: valAddr.String(),
Delegator: contractAddr.String(),
},
}}}}
reflectDelegationBin := buildMaskQuery(t, &reflectDelegationQuery)
reflectDelegationBin := buildReflectQuery(t, &reflectDelegationQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectDelegationBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response

View File

@@ -0,0 +1,452 @@
package keeper
import (
"encoding/json"
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"strconv"
"testing"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
// test handing of submessages, very closely related to the reflect_test
// Try a simple send, no gas limit to for a sanity check before trying table tests
func TestDispatchSubMsgSuccessCase(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, nil, nil)
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, deposit)
creatorBalance := deposit.Sub(contractStart)
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
// creator instantiates a contract and gives it tokens
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
require.NoError(t, err)
require.NotEmpty(t, contractAddr)
// check some account values
checkAccount(t, ctx, accKeeper, bankKeeper, contractAddr, contractStart)
checkAccount(t, ctx, accKeeper, bankKeeper, creator, creatorBalance)
checkAccount(t, ctx, accKeeper, bankKeeper, fred, nil)
// creator can send contract's tokens to fred (using SendMsg)
msg := wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: fred.String(),
Amount: []wasmvmtypes.Coin{{
Denom: "denom",
Amount: "15000",
}},
},
},
}
reflectSend := ReflectHandleMsg{
ReflectSubCall: &reflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: 7,
Msg: msg,
}},
},
}
reflectSendBz, err := json.Marshal(reflectSend)
require.NoError(t, err)
_, err = keeper.Execute(ctx, contractAddr, creator, reflectSendBz, nil)
require.NoError(t, err)
// fred got coins
checkAccount(t, ctx, accKeeper, bankKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 15000)))
// contract lost them
checkAccount(t, ctx, accKeeper, bankKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)))
checkAccount(t, ctx, accKeeper, bankKeeper, creator, creatorBalance)
// query the reflect state to ensure the result was stored
query := ReflectQueryMsg{
SubCallResult: &SubCall{ID: 7},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
queryRes, err := keeper.QuerySmart(ctx, contractAddr, queryBz)
require.NoError(t, err)
var res wasmvmtypes.Reply
err = json.Unmarshal(queryRes, &res)
require.NoError(t, err)
assert.Equal(t, uint64(7), res.ID)
assert.Empty(t, res.Result.Err)
require.NotNil(t, res.Result.Ok)
sub := res.Result.Ok
assert.Empty(t, sub.Data)
require.Len(t, sub.Events, 3)
transfer := sub.Events[0]
assert.Equal(t, "transfer", transfer.Type)
assert.Equal(t, wasmvmtypes.EventAttribute{
Key: "recipient",
Value: fred.String(),
}, transfer.Attributes[0])
sender := sub.Events[1]
assert.Equal(t, "message", sender.Type)
assert.Equal(t, wasmvmtypes.EventAttribute{
Key: "sender",
Value: contractAddr.String(),
}, sender.Attributes[0])
// where does this come from?
module := sub.Events[2]
assert.Equal(t, "message", module.Type)
assert.Equal(t, wasmvmtypes.EventAttribute{
Key: "module",
Value: "bank",
}, module.Attributes[0])
}
func TestDispatchSubMsgErrorHandling(t *testing.T) {
fundedDenom := "funds"
fundedAmount := 1_000_000
ctxGasLimit := uint64(1_000_000)
subGasLimit := uint64(300_000)
// prep - create one chain and upload the code
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, nil, nil)
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
contractStart := sdk.NewCoins(sdk.NewInt64Coin(fundedDenom, int64(fundedAmount)))
uploader := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, contractStart.Add(contractStart...))
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
reflectID, err := keeper.Create(ctx, uploader, reflectCode, "", "", nil)
require.NoError(t, err)
// create hackatom contract for testing (for infinite loop)
hackatomCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
hackatomID, err := keeper.Create(ctx, uploader, hackatomCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
_, _, fred := keyPubAddr()
initMsg := HackatomExampleInitMsg{
Verifier: fred,
Beneficiary: bob,
}
initMsgBz, err := json.Marshal(initMsg)
require.NoError(t, err)
hackatomAddr, _, err := keeper.Instantiate(ctx, hackatomID, uploader, nil, initMsgBz, "hackatom demo", contractStart)
require.NoError(t, err)
validBankSend := func(contract, emptyAccount string) wasmvmtypes.CosmosMsg {
return wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: emptyAccount,
Amount: []wasmvmtypes.Coin{{
Denom: fundedDenom,
Amount: strconv.Itoa(fundedAmount / 2),
}},
},
},
}
}
invalidBankSend := func(contract, emptyAccount string) wasmvmtypes.CosmosMsg {
return wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: emptyAccount,
Amount: []wasmvmtypes.Coin{{
Denom: fundedDenom,
Amount: strconv.Itoa(fundedAmount * 2),
}},
},
},
}
}
infiniteLoop := func(contract, emptyAccount string) wasmvmtypes.CosmosMsg {
return wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Execute: &wasmvmtypes.ExecuteMsg{
ContractAddr: hackatomAddr.String(),
Msg: []byte(`{"cpu_loop":{}}`),
},
},
}
}
instantiateContract := func(contract, emptyAccount string) wasmvmtypes.CosmosMsg {
return wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Instantiate: &wasmvmtypes.InstantiateMsg{
CodeID: reflectID,
Msg: []byte("{}"),
Label: "subcall reflect",
},
},
}
}
type assertion func(t *testing.T, ctx sdk.Context, contract, emptyAccount string, response wasmvmtypes.SubcallResult)
assertReturnedEvents := func(expectedEvents int) assertion {
return func(t *testing.T, ctx sdk.Context, contract, emptyAccount string, response wasmvmtypes.SubcallResult) {
assert.Len(t, response.Ok.Events, expectedEvents)
}
}
assertGasUsed := func(minGas, maxGas uint64) assertion {
return func(t *testing.T, ctx sdk.Context, contract, emptyAccount string, response wasmvmtypes.SubcallResult) {
gasUsed := ctx.GasMeter().GasConsumed()
assert.True(t, gasUsed >= minGas, "Used %d gas (less than expected %d)", gasUsed, minGas)
assert.True(t, gasUsed <= maxGas, "Used %d gas (more than expected %d)", gasUsed, maxGas)
}
}
assertErrorString := func(shouldContain string) assertion {
return func(t *testing.T, ctx sdk.Context, contract, emptyAccount string, response wasmvmtypes.SubcallResult) {
assert.Contains(t, response.Err, shouldContain)
}
}
assertGotContractAddr := func(t *testing.T, ctx sdk.Context, contract, emptyAccount string, response wasmvmtypes.SubcallResult) {
// should get the events emitted on new contract
event := response.Ok.Events[0]
assert.Equal(t, event.Type, "wasm")
assert.Equal(t, event.Attributes[0].Key, "contract_address")
eventAddr := event.Attributes[0].Value
assert.NotEqual(t, contract, eventAddr)
// data field is the raw canonical address
// QUESTION: why not types.MsgInstantiateContractResponse? difference between calling Router and Service?
assert.Len(t, response.Ok.Data, 20)
resAddr := sdk.AccAddress(response.Ok.Data)
assert.Equal(t, eventAddr, resAddr.String())
}
cases := map[string]struct {
submsgID uint64
// we will generate message from the
msg func(contract, emptyAccount string) wasmvmtypes.CosmosMsg
gasLimit *uint64
// true if we expect this to throw out of gas panic
isOutOfGasPanic bool
// true if we expect this execute to return an error (can be false when submessage errors)
executeError bool
// true if we expect submessage to return an error (but execute to return success)
subMsgError bool
// make assertions after dispatch
resultAssertions []assertion
}{
"send tokens": {
submsgID: 5,
msg: validBankSend,
// note we charge another 40k for the reply call
resultAssertions: []assertion{assertReturnedEvents(3), assertGasUsed(123000, 125000)},
},
"not enough tokens": {
submsgID: 6,
msg: invalidBankSend,
subMsgError: true,
// uses less gas than the send tokens (cost of bank transfer)
resultAssertions: []assertion{assertGasUsed(97000, 99000), assertErrorString("insufficient funds")},
},
"out of gas panic with no gas limit": {
submsgID: 7,
msg: infiniteLoop,
isOutOfGasPanic: true,
},
"send tokens with limit": {
submsgID: 15,
msg: validBankSend,
gasLimit: &subGasLimit,
// uses same gas as call without limit
resultAssertions: []assertion{assertReturnedEvents(3), assertGasUsed(123000, 125000)},
},
"not enough tokens with limit": {
submsgID: 16,
msg: invalidBankSend,
subMsgError: true,
gasLimit: &subGasLimit,
// uses same gas as call without limit
resultAssertions: []assertion{assertGasUsed(97000, 99000), assertErrorString("insufficient funds")},
},
"out of gas caught with gas limit": {
submsgID: 17,
msg: infiniteLoop,
subMsgError: true,
gasLimit: &subGasLimit,
// uses all the subGasLimit, plus the 92k or so for the main contract
resultAssertions: []assertion{assertGasUsed(subGasLimit+92000, subGasLimit+94000), assertErrorString("out of gas")},
},
"instantiate contract gets address in data and events": {
submsgID: 21,
msg: instantiateContract,
resultAssertions: []assertion{assertReturnedEvents(1), assertGotContractAddr},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, contractStart)
_, _, empty := keyPubAddr()
contractAddr, _, err := keeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), fmt.Sprintf("contract %s", name), contractStart)
require.NoError(t, err)
msg := tc.msg(contractAddr.String(), empty.String())
reflectSend := ReflectHandleMsg{
ReflectSubCall: &reflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: tc.submsgID,
Msg: msg,
GasLimit: tc.gasLimit,
}},
},
}
reflectSendBz, err := json.Marshal(reflectSend)
require.NoError(t, err)
execCtx := ctx.WithGasMeter(sdk.NewGasMeter(ctxGasLimit))
defer func() {
if tc.isOutOfGasPanic {
r := recover()
require.NotNil(t, r, "expected panic")
if _, ok := r.(sdk.ErrorOutOfGas); !ok {
t.Fatalf("Expected OutOfGas panic, got: %#v\n", r)
}
}
}()
_, err = keeper.Execute(execCtx, contractAddr, creator, reflectSendBz, nil)
if tc.executeError {
require.Error(t, err)
} else {
require.NoError(t, err)
// query the reply
query := ReflectQueryMsg{
SubCallResult: &SubCall{ID: tc.submsgID},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
queryRes, err := keeper.QuerySmart(ctx, contractAddr, queryBz)
require.NoError(t, err)
var res wasmvmtypes.Reply
err = json.Unmarshal(queryRes, &res)
require.NoError(t, err)
assert.Equal(t, tc.submsgID, res.ID)
if tc.subMsgError {
require.NotEmpty(t, res.Result.Err)
require.Nil(t, res.Result.Ok)
} else {
require.Empty(t, res.Result.Err)
require.NotNil(t, res.Result.Ok)
}
for _, assertion := range tc.resultAssertions {
assertion(t, execCtx, contractAddr.String(), empty.String(), res.Result)
}
}
})
}
}
// Test an error case, where the Encoded doesn't return any sdk.Msg and we trigger(ed) a null pointer exception.
// This occurs with the IBC encoder. Test this.
func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) {
// fake out the bank handle to return success with no data
nilEncoder := func(sender sdk.AccAddress, msg *wasmvmtypes.BankMsg) ([]sdk.Msg, error) {
return nil, nil
}
customEncoders := &MessageEncoders{
Bank: nilEncoder,
}
ctx, keepers := CreateTestInput(t, false, ReflectFeatures, customEncoders, nil)
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, deposit)
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, reflectCode, "", "", nil)
require.NoError(t, err)
// creator instantiates a contract and gives it tokens
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart)
require.NoError(t, err)
require.NotEmpty(t, contractAddr)
// creator can send contract's tokens to fred (using SendMsg)
msg := wasmvmtypes.CosmosMsg{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: fred.String(),
Amount: []wasmvmtypes.Coin{{
Denom: "denom",
Amount: "15000",
}},
},
},
}
reflectSend := ReflectHandleMsg{
ReflectSubCall: &reflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: 7,
Msg: msg,
}},
},
}
reflectSendBz, err := json.Marshal(reflectSend)
require.NoError(t, err)
_, err = keeper.Execute(ctx, contractAddr, creator, reflectSendBz, nil)
require.NoError(t, err)
// query the reflect state to ensure the result was stored
query := ReflectQueryMsg{
SubCallResult: &SubCall{ID: 7},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
queryRes, err := keeper.QuerySmart(ctx, contractAddr, queryBz)
require.NoError(t, err)
var res wasmvmtypes.Reply
err = json.Unmarshal(queryRes, &res)
require.NoError(t, err)
assert.Equal(t, uint64(7), res.ID)
assert.Empty(t, res.Result.Err)
require.NotNil(t, res.Result.Ok)
sub := res.Result.Ok
assert.Empty(t, sub.Data)
require.Len(t, sub.Events, 0)
}

View File

@@ -1,24 +1,35 @@
package wasmtesting
import (
"errors"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type MockMessageHandler struct {
DispatchFn func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error
DispatchMsgFn func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error)
}
func (m *MockMessageHandler) Dispatch(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
return m.DispatchFn(ctx, contractAddr, contractIBCPortID, msgs...)
func (m *MockMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
return m.DispatchMsgFn(ctx, contractAddr, contractIBCPortID, msg)
}
func NewCapturingMessageHandler() (*MockMessageHandler, *[]wasmvmtypes.CosmosMsg) {
var messages []wasmvmtypes.CosmosMsg
return &MockMessageHandler{
DispatchFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msgs ...wasmvmtypes.CosmosMsg) error {
messages = append(messages, msgs...)
return nil
DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
messages = append(messages, msg)
// return one data item so that this doesn't cause an error in submessage processing (it takes the first element from data)
return nil, [][]byte{{1}}, nil
},
}, &messages
}
func NewErroringMessageHandler() *MockMessageHandler {
return &MockMessageHandler{
DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) {
return nil, nil, errors.New("test, ignore")
},
}
}

View File

@@ -22,6 +22,7 @@ type MockWasmer struct {
QueryFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) ([]byte, uint64, error)
MigrateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.Response, uint64, error)
SudoFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.Response, uint64, error)
ReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.Response, uint64, error)
GetCodeFn func(codeID wasmvm.Checksum) (wasmvm.WasmCode, error)
CleanupFn func()
IBCChannelOpenFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannel, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (uint64, error)
@@ -126,6 +127,13 @@ func (m *MockWasmer) Sudo(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg [
}
func (m *MockWasmer) Reply(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.Response, uint64, error) {
if m.ReplyFn == nil {
panic("not supposed to be called!")
}
return m.ReplyFn(codeID, env, reply, store, goapi, querier, gasMeter, gasLimit)
}
func (m *MockWasmer) GetCode(codeID wasmvm.Checksum) (wasmvm.WasmCode, error) {
if m.GetCodeFn == nil {
panic("not supposed to be called!")

View File

@@ -107,6 +107,18 @@ type WasmerEngine interface {
gasLimit uint64,
) (*wasmvmtypes.Response, uint64, error)
// Reply is called on the original dispatching contract after running a submessage
Reply(
codeID wasmvm.Checksum,
env wasmvmtypes.Env,
reply wasmvmtypes.Reply,
store wasmvm.KVStore,
goapi wasmvm.GoAPI,
querier wasmvm.Querier,
gasMeter wasmvm.GasMeter,
gasLimit uint64,
) (*wasmvmtypes.Response, uint64, error)
// GetCode will load the original wasm code for the given code id.
// This will only succeed if that code id was previously returned from
// a call to Create.