mirror of https://github.com/certusone/wasmd.git
748 lines
29 KiB
Go
748 lines
29 KiB
Go
package keeper
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"math"
|
|
"testing"
|
|
|
|
wasmvm "github.com/CosmWasm/wasmvm/v3"
|
|
wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
storetypes "cosmossdk.io/store/types"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
"github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting"
|
|
"github.com/CosmWasm/wasmd/x/wasm/types"
|
|
)
|
|
|
|
func TestOnOpenChannel(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractGas storetypes.Gas
|
|
contractErr error
|
|
expGas uint64
|
|
expErr bool
|
|
}{
|
|
"consume contract gas": {
|
|
contractAddr: example.Contract,
|
|
contractGas: myContractGas,
|
|
expGas: myContractGas,
|
|
},
|
|
"consume max gas": {
|
|
contractAddr: example.Contract,
|
|
contractGas: math.MaxUint64 / types.DefaultGasMultiplier,
|
|
expGas: math.MaxUint64 / types.DefaultGasMultiplier,
|
|
},
|
|
"consume gas on error": {
|
|
contractAddr: example.Contract,
|
|
contractGas: myContractGas,
|
|
contractErr: errors.New("test, ignore"),
|
|
expErr: true,
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"}
|
|
myMsg := wasmvmtypes.IBCChannelOpenMsg{OpenTry: &wasmvmtypes.IBCOpenTry{Channel: myChannel, CounterpartyVersion: "foo"}}
|
|
m.IBCChannelOpenFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCChannelOpenResult, uint64, error) {
|
|
assert.Equal(t, myMsg, msg)
|
|
return &wasmvmtypes.IBCChannelOpenResult{Ok: &wasmvmtypes.IBC3ChannelOpenResponse{}}, spec.contractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
before := ctx.GasMeter().GasConsumed()
|
|
|
|
// when
|
|
msg := wasmvmtypes.IBCChannelOpenMsg{
|
|
OpenTry: &wasmvmtypes.IBCOpenTry{
|
|
Channel: myChannel,
|
|
CounterpartyVersion: "foo",
|
|
},
|
|
}
|
|
_, err := keepers.WasmKeeper.OnOpenChannel(ctx, spec.contractAddr, msg)
|
|
|
|
// then
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnConnectChannel(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractResp *wasmvmtypes.IBCBasicResponse
|
|
contractErr error
|
|
overwriteMessenger *wasmtesting.MockMessageHandler
|
|
expContractGas storetypes.Gas
|
|
expErr bool
|
|
expEventTypes []string
|
|
}{
|
|
"consume contract gas": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
|
},
|
|
"consume gas on error, ignore events + messages": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
contractErr: errors.New("test, ignore"),
|
|
expErr: true,
|
|
},
|
|
"dispatch contract messages on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
},
|
|
},
|
|
"emit contract events on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"messenger errors returned, events stored": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
|
|
expErr: true,
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"}
|
|
myMsg := wasmvmtypes.IBCChannelConnectMsg{OpenConfirm: &wasmvmtypes.IBCOpenConfirm{Channel: myChannel}}
|
|
m.IBCChannelConnectFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResult, uint64, error) {
|
|
assert.Equal(t, msg, myMsg)
|
|
return &wasmvmtypes.IBCBasicResult{Ok: spec.contractResp}, myContractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
|
|
|
before := ctx.GasMeter().GasConsumed()
|
|
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
|
*messenger = *msger
|
|
if spec.overwriteMessenger != nil {
|
|
*messenger = *spec.overwriteMessenger
|
|
}
|
|
|
|
// when
|
|
msg := wasmvmtypes.IBCChannelConnectMsg{
|
|
OpenConfirm: &wasmvmtypes.IBCOpenConfirm{
|
|
Channel: myChannel,
|
|
},
|
|
}
|
|
|
|
err := keepers.WasmKeeper.OnConnectChannel(ctx, spec.contractAddr, msg)
|
|
|
|
// then
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
assert.Empty(t, capturedMsgs) // no messages captured on error
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
// verify msgs dispatched
|
|
require.Len(t, *capturedMsgs, len(spec.contractResp.Messages))
|
|
for i, m := range spec.contractResp.Messages {
|
|
assert.Equal(t, (*capturedMsgs)[i], m.Msg)
|
|
}
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnCloseChannel(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractResp *wasmvmtypes.IBCBasicResponse
|
|
contractErr error
|
|
overwriteMessenger *wasmtesting.MockMessageHandler
|
|
expContractGas storetypes.Gas
|
|
expErr bool
|
|
expEventTypes []string
|
|
}{
|
|
"consume contract gas": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
|
},
|
|
"consume gas on error, ignore events + messages": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
contractErr: errors.New("test, ignore"),
|
|
expErr: true,
|
|
},
|
|
"dispatch contract messages on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
},
|
|
},
|
|
"emit contract events on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"messenger errors returned, events stored": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
|
|
expErr: true,
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"}
|
|
myMsg := wasmvmtypes.IBCChannelCloseMsg{CloseInit: &wasmvmtypes.IBCCloseInit{Channel: myChannel}}
|
|
m.IBCChannelCloseFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResult, uint64, error) {
|
|
assert.Equal(t, msg, myMsg)
|
|
return &wasmvmtypes.IBCBasicResult{Ok: spec.contractResp}, myContractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
before := ctx.GasMeter().GasConsumed()
|
|
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
|
*messenger = *msger
|
|
|
|
if spec.overwriteMessenger != nil {
|
|
*messenger = *spec.overwriteMessenger
|
|
}
|
|
|
|
// when
|
|
msg := wasmvmtypes.IBCChannelCloseMsg{
|
|
CloseInit: &wasmvmtypes.IBCCloseInit{
|
|
Channel: myChannel,
|
|
},
|
|
}
|
|
err := keepers.WasmKeeper.OnCloseChannel(ctx, spec.contractAddr, msg)
|
|
|
|
// then
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
assert.Empty(t, capturedMsgs) // no messages captured on error
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
// verify msgs dispatched
|
|
require.Len(t, *capturedMsgs, len(spec.contractResp.Messages))
|
|
for i, m := range spec.contractResp.Messages {
|
|
assert.Equal(t, (*capturedMsgs)[i], m.Msg)
|
|
}
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnRecvPacket(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
const storageCosts = storetypes.Gas(3119)
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractResp *wasmvmtypes.IBCReceiveResult
|
|
contractErr error
|
|
overwriteMessenger *wasmtesting.MockMessageHandler
|
|
mockReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error)
|
|
expContractGas storetypes.Gas
|
|
expAck []byte
|
|
expErr bool
|
|
expPanic bool
|
|
expEventTypes []string
|
|
}{
|
|
"contract returns success ack": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{Acknowledgement: []byte("myAck")},
|
|
},
|
|
expAck: []byte("myAck"),
|
|
},
|
|
"can return empty ack data": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{Acknowledgement: []byte{}},
|
|
},
|
|
expAck: []byte{},
|
|
},
|
|
"can return nil ack": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 2720, // 2720 is the cost of storing the packet
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{},
|
|
},
|
|
},
|
|
"contract Err result converted to error Ack": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Err: "my-error",
|
|
},
|
|
expAck: []byte(`{"error":"my-error"}`), // without error msg redaction
|
|
},
|
|
"contract aborts tx with error": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractErr: errors.New("test, ignore"),
|
|
expPanic: true,
|
|
},
|
|
"dispatch contract messages on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{
|
|
Acknowledgement: []byte("myAck"),
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
},
|
|
},
|
|
expAck: []byte("myAck"),
|
|
},
|
|
"emit contract attributes on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{
|
|
Acknowledgement: []byte("myAck"),
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
expAck: []byte("myAck"),
|
|
},
|
|
"emit contract events on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 46, // charge or custom event as well
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{
|
|
Acknowledgement: []byte("myAck"),
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
Events: []wasmvmtypes.Event{{
|
|
Type: "custom",
|
|
Attributes: []wasmvmtypes.EventAttribute{{
|
|
Key: "message",
|
|
Value: "to rudi",
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType, "wasm-custom"},
|
|
expAck: []byte("myAck"),
|
|
},
|
|
"messenger errors returned": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{
|
|
Acknowledgement: []byte("myAck"),
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
},
|
|
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
|
|
expErr: true,
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"submessage reply can overwrite ack data": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: types.DefaultInstanceCostDiscount + myContractGas + storageCosts,
|
|
contractResp: &wasmvmtypes.IBCReceiveResult{
|
|
Ok: &wasmvmtypes.IBCReceiveResponse{
|
|
Acknowledgement: []byte("myAck"),
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyAlways, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}},
|
|
},
|
|
},
|
|
mockReplyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
|
|
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{Data: []byte("myBetterAck")}}, 0, nil
|
|
},
|
|
expAck: []byte("myBetterAck"),
|
|
expEventTypes: []string{types.EventTypeReply},
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myPacket := wasmvmtypes.IBCPacket{Data: []byte("my data")}
|
|
|
|
m.IBCPacketReceiveFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) {
|
|
assert.Equal(t, myPacket, msg.Packet)
|
|
return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
if spec.mockReplyFn != nil {
|
|
m.ReplyFn = spec.mockReplyFn
|
|
h, ok := keepers.WasmKeeper.wasmVMResponseHandler.(*DefaultWasmVMContractResponseHandler)
|
|
require.True(t, ok)
|
|
h.md = NewMessageDispatcher(messenger, keepers.WasmKeeper)
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
before := ctx.GasMeter().GasConsumed()
|
|
|
|
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
|
*messenger = *msger
|
|
|
|
if spec.overwriteMessenger != nil {
|
|
*messenger = *spec.overwriteMessenger
|
|
}
|
|
|
|
// when
|
|
msg := wasmvmtypes.IBCPacketReceiveMsg{Packet: myPacket}
|
|
if spec.expPanic {
|
|
assert.Panics(t, func() {
|
|
_, _ = keepers.WasmKeeper.OnRecvPacket(ctx, spec.contractAddr, msg)
|
|
})
|
|
return
|
|
}
|
|
gotAck, err := keepers.WasmKeeper.OnRecvPacket(ctx, spec.contractAddr, msg)
|
|
|
|
// then
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
assert.Empty(t, capturedMsgs) // no messages captured on error
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if spec.expAck != nil {
|
|
require.Equal(t, spec.expAck, gotAck.Acknowledgement())
|
|
} else {
|
|
require.Nil(t, gotAck)
|
|
}
|
|
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
|
|
// verify msgs dispatched on success/ err response
|
|
if spec.contractResp.Err != "" {
|
|
assert.Empty(t, capturedMsgs) // no messages captured on err response
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
} else {
|
|
require.Len(t, *capturedMsgs, len(spec.contractResp.Ok.Messages))
|
|
for i, m := range spec.contractResp.Ok.Messages {
|
|
assert.Equal(t, (*capturedMsgs)[i], m.Msg)
|
|
}
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnAckPacket(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractResp *wasmvmtypes.IBCBasicResponse
|
|
contractErr error
|
|
overwriteMessenger *wasmtesting.MockMessageHandler
|
|
expContractGas storetypes.Gas
|
|
expErr bool
|
|
expEventTypes []string
|
|
}{
|
|
"consume contract gas": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
|
},
|
|
"consume gas on error, ignore events + messages": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
contractErr: errors.New("test, ignore"),
|
|
expErr: true,
|
|
},
|
|
"dispatch contract messages on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
},
|
|
},
|
|
"emit contract events on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"messenger errors returned, events stored": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
|
|
expErr: true,
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myAck := wasmvmtypes.IBCPacketAckMsg{Acknowledgement: wasmvmtypes.IBCAcknowledgement{Data: []byte("myAck")}}
|
|
m.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResult, uint64, error) {
|
|
assert.Equal(t, myAck, msg)
|
|
return &wasmvmtypes.IBCBasicResult{Ok: spec.contractResp}, myContractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
before := ctx.GasMeter().GasConsumed()
|
|
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
|
*messenger = *msger
|
|
|
|
if spec.overwriteMessenger != nil {
|
|
*messenger = *spec.overwriteMessenger
|
|
}
|
|
|
|
// when
|
|
err := keepers.WasmKeeper.OnAckPacket(ctx, spec.contractAddr, myAck)
|
|
|
|
// then
|
|
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
assert.Empty(t, capturedMsgs) // no messages captured on error
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
// verify msgs dispatched
|
|
require.Len(t, *capturedMsgs, len(spec.contractResp.Messages))
|
|
for i, m := range spec.contractResp.Messages {
|
|
assert.Equal(t, (*capturedMsgs)[i], m.Msg)
|
|
}
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnTimeoutPacket(t *testing.T) {
|
|
var m wasmtesting.MockWasmEngine
|
|
wasmtesting.MakeIBCInstantiable(&m)
|
|
messenger := &wasmtesting.MockMessageHandler{}
|
|
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities, WithMessageHandler(messenger))
|
|
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
|
const myContractGas = 40
|
|
|
|
specs := map[string]struct {
|
|
contractAddr sdk.AccAddress
|
|
contractResp *wasmvmtypes.IBCBasicResponse
|
|
contractErr error
|
|
overwriteMessenger *wasmtesting.MockMessageHandler
|
|
expContractGas storetypes.Gas
|
|
expErr bool
|
|
expEventTypes []string
|
|
}{
|
|
"consume contract gas": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
|
},
|
|
"consume gas on error, ignore events + messages": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
contractErr: errors.New("test, ignore"),
|
|
expErr: true,
|
|
},
|
|
"dispatch contract messages on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
},
|
|
},
|
|
"emit contract attributes on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"emit contract events on success": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 46, // cost for custom events
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
Events: []wasmvmtypes.Event{{
|
|
Type: "custom",
|
|
Attributes: []wasmvmtypes.EventAttribute{{
|
|
Key: "message",
|
|
Value: "to rudi",
|
|
}},
|
|
}},
|
|
},
|
|
expEventTypes: []string{types.WasmModuleEventType, "wasm-custom"},
|
|
},
|
|
"messenger errors returned, events stored before": {
|
|
contractAddr: example.Contract,
|
|
expContractGas: myContractGas + 10,
|
|
contractResp: &wasmvmtypes.IBCBasicResponse{
|
|
Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}, {ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{Custom: json.RawMessage(`{"foo":"bar"}`)}}},
|
|
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
|
},
|
|
overwriteMessenger: wasmtesting.NewErroringMessageHandler(),
|
|
expErr: true,
|
|
expEventTypes: []string{types.WasmModuleEventType},
|
|
},
|
|
"unknown contract address": {
|
|
contractAddr: RandomAccountAddress(t),
|
|
expErr: true,
|
|
},
|
|
}
|
|
for name, spec := range specs {
|
|
t.Run(name, func(t *testing.T) {
|
|
myPacket := wasmvmtypes.IBCPacket{Data: []byte("my test packet")}
|
|
m.IBCPacketTimeoutFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResult, uint64, error) {
|
|
assert.Equal(t, myPacket, msg.Packet)
|
|
return &wasmvmtypes.IBCBasicResult{Ok: spec.contractResp}, myContractGas * types.DefaultGasMultiplier, spec.contractErr
|
|
}
|
|
|
|
ctx, _ := parentCtx.CacheContext()
|
|
before := ctx.GasMeter().GasConsumed()
|
|
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
|
*messenger = *msger
|
|
|
|
if spec.overwriteMessenger != nil {
|
|
*messenger = *spec.overwriteMessenger
|
|
}
|
|
|
|
// when
|
|
msg := wasmvmtypes.IBCPacketTimeoutMsg{Packet: myPacket}
|
|
err := keepers.WasmKeeper.OnTimeoutPacket(ctx, spec.contractAddr, msg)
|
|
|
|
// then
|
|
if spec.expErr {
|
|
require.Error(t, err)
|
|
assert.Empty(t, capturedMsgs) // no messages captured on error
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
// verify gas consumed
|
|
const storageCosts = storetypes.Gas(3119)
|
|
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
|
// verify msgs dispatched
|
|
require.Len(t, *capturedMsgs, len(spec.contractResp.Messages))
|
|
for i, m := range spec.contractResp.Messages {
|
|
assert.Equal(t, (*capturedMsgs)[i], m.Msg)
|
|
}
|
|
assert.Equal(t, spec.expEventTypes, stripTypes(ctx.EventManager().Events()))
|
|
})
|
|
}
|
|
}
|
|
|
|
func stripTypes(events sdk.Events) []string {
|
|
var r []string
|
|
for _, e := range events {
|
|
r = append(r, e.Type)
|
|
}
|
|
return r
|
|
}
|