mirror of https://github.com/certusone/wasmd.git
Charge gas for custom event attributes and messages (#539)
* Charge gas for custom event attributes * Introduce gas register for gas costs * Review feedback * Tests and minor updates * Godoc
This commit is contained in:
parent
e8797cbfcd
commit
c05df881fb
|
@ -0,0 +1,187 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"github.com/CosmWasm/wasmd/x/wasm/types"
|
||||
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultGasMultiplier is how many cosmwasm gas points = 1 sdk gas point
|
||||
// SDK reference costs can be found here: https://github.com/cosmos/cosmos-sdk/blob/02c6c9fafd58da88550ab4d7d494724a477c8a68/store/types/gas.go#L153-L164
|
||||
// A write at ~3000 gas and ~200us = 10 gas per us (microsecond) cpu/io
|
||||
// Rough timing have 88k gas at 90us, which is equal to 1k sdk gas... (one read)
|
||||
//
|
||||
// Please note that all gas prices returned to the wasmer engine should have this multiplied
|
||||
DefaultGasMultiplier uint64 = 100
|
||||
// DefaultInstanceCost is how much SDK gas we charge each time we load a WASM instance.
|
||||
// Creating a new instance is costly, and this helps put a recursion limit to contracts calling contracts.
|
||||
DefaultInstanceCost uint64 = 40_000
|
||||
// DefaultCompileCost is how much SDK gas is charged *per byte* for compiling WASM code.
|
||||
DefaultCompileCost uint64 = 2
|
||||
// DefaultEventAttributeDataCost is how much SDK gas is charged *per byte* for attribute data in events.
|
||||
// This is used with len(key) + len(value)
|
||||
DefaultEventAttributeDataCost uint64 = 1
|
||||
// DefaultContractMessageDataCost is how much SDK gas is charged *per byte* of the message that goes to the contract
|
||||
// This is used with len(msg)
|
||||
DefaultContractMessageDataCost uint64 = 1
|
||||
// DefaultPerAttributeCost is how much SDK gas we charge per attribute count.
|
||||
DefaultPerAttributeCost uint64 = 10
|
||||
// DefaultEventAttributeDataFreeTier number of bytes of total attribute data we do not charge.
|
||||
DefaultEventAttributeDataFreeTier = 100
|
||||
)
|
||||
|
||||
// GasRegister abstract source for gas costs
|
||||
type GasRegister interface {
|
||||
// NewContractInstanceCosts costs to crate a new contract instance from code
|
||||
NewContractInstanceCosts(pinned bool, msgLen int) sdk.Gas
|
||||
// CompileCosts costs to persist and "compile" a new wasm contract
|
||||
CompileCosts(byteLength int) sdk.Gas
|
||||
// InstantiateContractCosts costs when interacting with a wasm contract
|
||||
InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas
|
||||
// ReplyCosts costs to to handle a message reply
|
||||
ReplyCosts(pinned bool, reply wasmvmtypes.Reply) sdk.Gas
|
||||
// EventCosts costs to persist an event
|
||||
EventCosts(evts []wasmvmtypes.EventAttribute) sdk.Gas
|
||||
// ToWasmVMGas converts from sdk gas to wasmvm gas
|
||||
ToWasmVMGas(source sdk.Gas) uint64
|
||||
// FromWasmVMGas converts from wasmvm gas to sdk gas
|
||||
FromWasmVMGas(source uint64) sdk.Gas
|
||||
}
|
||||
|
||||
// WasmGasRegisterConfig config type
|
||||
type WasmGasRegisterConfig struct {
|
||||
// InstanceCost costs when interacting with a wasm contract
|
||||
InstanceCost sdk.Gas
|
||||
// CompileCosts costs to persist and "compile" a new wasm contract
|
||||
CompileCost sdk.Gas
|
||||
// GasMultiplier is how many cosmwasm gas points = 1 sdk gas point
|
||||
// SDK reference costs can be found here: https://github.com/cosmos/cosmos-sdk/blob/02c6c9fafd58da88550ab4d7d494724a477c8a68/store/types/gas.go#L153-L164
|
||||
GasMultiplier sdk.Gas
|
||||
// EventPerAttributeCost is how much SDK gas is charged *per byte* for attribute data in events.
|
||||
// This is used with len(key) + len(value)
|
||||
EventPerAttributeCost sdk.Gas
|
||||
// EventAttributeDataCost is how much SDK gas is charged *per byte* for attribute data in events.
|
||||
// This is used with len(key) + len(value)
|
||||
EventAttributeDataCost sdk.Gas
|
||||
// EventAttributeDataFreeTier number of bytes of total attribute data that is free of charge
|
||||
EventAttributeDataFreeTier int
|
||||
// ContractMessageDataCost SDK gas charged *per byte* of the message that goes to the contract
|
||||
// This is used with len(msg)
|
||||
ContractMessageDataCost sdk.Gas
|
||||
}
|
||||
|
||||
// DefaultGasRegisterConfig default values
|
||||
func DefaultGasRegisterConfig() WasmGasRegisterConfig {
|
||||
return WasmGasRegisterConfig{
|
||||
InstanceCost: DefaultInstanceCost,
|
||||
CompileCost: DefaultCompileCost,
|
||||
GasMultiplier: DefaultGasMultiplier,
|
||||
EventPerAttributeCost: DefaultPerAttributeCost,
|
||||
EventAttributeDataCost: DefaultEventAttributeDataCost,
|
||||
EventAttributeDataFreeTier: DefaultEventAttributeDataFreeTier,
|
||||
ContractMessageDataCost: DefaultContractMessageDataCost,
|
||||
}
|
||||
}
|
||||
|
||||
// WasmGasRegister implements GasRegister interface
|
||||
type WasmGasRegister struct {
|
||||
c WasmGasRegisterConfig
|
||||
}
|
||||
|
||||
// NewDefaultWasmGasRegister creates instance with default values
|
||||
func NewDefaultWasmGasRegister() WasmGasRegister {
|
||||
return NewWasmGasRegister(DefaultGasRegisterConfig())
|
||||
}
|
||||
|
||||
// NewWasmGasRegister constructor
|
||||
func NewWasmGasRegister(c WasmGasRegisterConfig) WasmGasRegister {
|
||||
if c.GasMultiplier == 0 {
|
||||
panic(sdkerrors.Wrap(sdkerrors.ErrLogic, "GasMultiplier can not be 0"))
|
||||
}
|
||||
return WasmGasRegister{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
// NewContractInstanceCosts costs to crate a new contract instance from code
|
||||
func (g WasmGasRegister) NewContractInstanceCosts(pinned bool, msgLen int) storetypes.Gas {
|
||||
return g.InstantiateContractCosts(pinned, msgLen)
|
||||
}
|
||||
|
||||
// CompileCosts costs to persist and "compile" a new wasm contract
|
||||
func (g WasmGasRegister) CompileCosts(byteLength int) storetypes.Gas {
|
||||
if byteLength < 0 {
|
||||
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
|
||||
}
|
||||
return g.c.CompileCost * uint64(byteLength)
|
||||
}
|
||||
|
||||
// InstantiateContractCosts costs when interacting with a wasm contract
|
||||
func (g WasmGasRegister) InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas {
|
||||
if msgLen < 0 {
|
||||
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
|
||||
}
|
||||
dataCosts := sdk.Gas(msgLen) * g.c.ContractMessageDataCost
|
||||
if pinned {
|
||||
return dataCosts
|
||||
}
|
||||
return g.c.InstanceCost + dataCosts
|
||||
}
|
||||
|
||||
// ReplyCosts costs to to handle a message reply
|
||||
func (g WasmGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) sdk.Gas {
|
||||
var eventGas sdk.Gas
|
||||
msgLen := len(reply.Result.Err)
|
||||
if reply.Result.Ok != nil {
|
||||
msgLen += len(reply.Result.Ok.Data)
|
||||
var attrs []wasmvmtypes.EventAttribute
|
||||
for _, e := range reply.Result.Ok.Events {
|
||||
msgLen += len(e.Type)
|
||||
attrs = append(e.Attributes)
|
||||
}
|
||||
// apply free tier on the whole set not per event
|
||||
eventGas += g.EventCosts(attrs)
|
||||
}
|
||||
return eventGas + g.InstantiateContractCosts(pinned, msgLen)
|
||||
}
|
||||
|
||||
// EventCosts costs to persist an event
|
||||
func (g WasmGasRegister) EventCosts(evts []wasmvmtypes.EventAttribute) sdk.Gas {
|
||||
if len(evts) == 0 {
|
||||
return 0
|
||||
}
|
||||
var storedBytes int
|
||||
for _, l := range evts {
|
||||
storedBytes += len(l.Key) + len(l.Value)
|
||||
}
|
||||
// apply free tier
|
||||
if storedBytes <= g.c.EventAttributeDataFreeTier {
|
||||
storedBytes = 0
|
||||
} else {
|
||||
storedBytes -= g.c.EventAttributeDataFreeTier
|
||||
}
|
||||
// total Length * costs + attribute count * costs
|
||||
r := sdk.NewIntFromUint64(g.c.EventAttributeDataCost).Mul(sdk.NewIntFromUint64(uint64(storedBytes))).
|
||||
Add(sdk.NewIntFromUint64(g.c.EventPerAttributeCost).Mul(sdk.NewIntFromUint64(uint64(len(evts)))))
|
||||
if !r.IsUint64() {
|
||||
panic(sdk.ErrorOutOfGas{Descriptor: "overflow"})
|
||||
}
|
||||
return r.Uint64()
|
||||
}
|
||||
|
||||
// ToWasmVMGas convert to wasmVM contract runtime gas unit
|
||||
func (g WasmGasRegister) ToWasmVMGas(source storetypes.Gas) uint64 {
|
||||
x := source * g.c.GasMultiplier
|
||||
if x < source {
|
||||
panic(sdk.ErrorOutOfGas{Descriptor: "overflow"})
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// FromWasmVMGas converts to SDK gas unit
|
||||
func (g WasmGasRegister) FromWasmVMGas(source uint64) sdk.Gas {
|
||||
return source / g.c.GasMultiplier
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompileCosts(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
srcLen int
|
||||
srcConfig WasmGasRegisterConfig
|
||||
exp sdk.Gas
|
||||
expPanic bool
|
||||
}{
|
||||
"one byte": {
|
||||
srcLen: 1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(2), // DefaultCompileCost
|
||||
},
|
||||
"zero byte": {
|
||||
srcLen: 0,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(0),
|
||||
},
|
||||
"negative len": {
|
||||
srcLen: -1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
expPanic: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
NewWasmGasRegister(spec.srcConfig).CompileCosts(spec.srcLen)
|
||||
})
|
||||
return
|
||||
}
|
||||
gotGas := NewWasmGasRegister(spec.srcConfig).CompileCosts(spec.srcLen)
|
||||
assert.Equal(t, spec.exp, gotGas)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewContractInstanceCosts(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
srcLen int
|
||||
srcConfig WasmGasRegisterConfig
|
||||
pinned bool
|
||||
exp sdk.Gas
|
||||
expPanic bool
|
||||
}{
|
||||
"small msg - pinned": {
|
||||
srcLen: 1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(1),
|
||||
},
|
||||
"big msg - pinned": {
|
||||
srcLen: math.MaxUint32,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(math.MaxUint32),
|
||||
},
|
||||
"empty msg - pinned": {
|
||||
srcLen: 0,
|
||||
pinned: true,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(0),
|
||||
},
|
||||
"small msg - unpinned": {
|
||||
srcLen: 1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(1),
|
||||
},
|
||||
"big msg - unpinned": {
|
||||
srcLen: math.MaxUint32,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(math.MaxUint32 + 40_000),
|
||||
},
|
||||
"empty msg - unpinned": {
|
||||
srcLen: 0,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000),
|
||||
},
|
||||
|
||||
"negative len": {
|
||||
srcLen: -1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
expPanic: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
NewWasmGasRegister(spec.srcConfig).NewContractInstanceCosts(spec.pinned, spec.srcLen)
|
||||
})
|
||||
return
|
||||
}
|
||||
gotGas := NewWasmGasRegister(spec.srcConfig).NewContractInstanceCosts(spec.pinned, spec.srcLen)
|
||||
assert.Equal(t, spec.exp, gotGas)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContractInstanceCosts(t *testing.T) {
|
||||
// same as TestNewContractInstanceCosts currently
|
||||
specs := map[string]struct {
|
||||
srcLen int
|
||||
srcConfig WasmGasRegisterConfig
|
||||
pinned bool
|
||||
exp sdk.Gas
|
||||
expPanic bool
|
||||
}{
|
||||
"small msg - pinned": {
|
||||
srcLen: 1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(1),
|
||||
},
|
||||
"big msg - pinned": {
|
||||
srcLen: math.MaxUint32,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(math.MaxUint32),
|
||||
},
|
||||
"empty msg - pinned": {
|
||||
srcLen: 0,
|
||||
pinned: true,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(0),
|
||||
},
|
||||
"small msg - unpinned": {
|
||||
srcLen: 1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(1),
|
||||
},
|
||||
"big msg - unpinned": {
|
||||
srcLen: math.MaxUint32,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(math.MaxUint32 + 40_000),
|
||||
},
|
||||
"empty msg - unpinned": {
|
||||
srcLen: 0,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000),
|
||||
},
|
||||
|
||||
"negative len": {
|
||||
srcLen: -1,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
expPanic: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
NewWasmGasRegister(spec.srcConfig).InstantiateContractCosts(spec.pinned, spec.srcLen)
|
||||
})
|
||||
return
|
||||
}
|
||||
gotGas := NewWasmGasRegister(spec.srcConfig).InstantiateContractCosts(spec.pinned, spec.srcLen)
|
||||
assert.Equal(t, spec.exp, gotGas)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplyCost(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
src wasmvmtypes.Reply
|
||||
srcConfig WasmGasRegisterConfig
|
||||
pinned bool
|
||||
exp sdk.Gas
|
||||
expPanic bool
|
||||
}{
|
||||
"subcall response with events and data - pinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
|
||||
},
|
||||
Data: []byte{0x1},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(3 + 10 + 1), // len("foo") + 1 * DefaultPerAttributeCost + len(data)
|
||||
},
|
||||
"subcall response with events - pinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(3 + 10), // len("foo") + 1 * DefaultPerAttributeCost
|
||||
},
|
||||
"subcall response with events exceeds free tier- pinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: strings.Repeat("x", DefaultEventAttributeDataFreeTier), Value: "myData"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(3 + 10 + 6), // len("foo") + 1 * DefaultPerAttributeCost + len("myData")
|
||||
},
|
||||
"subcall response error - pinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Err: "foo",
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
pinned: true,
|
||||
exp: sdk.Gas(3), // len("foo")
|
||||
},
|
||||
"subcall response with events and data - unpinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
|
||||
},
|
||||
Data: []byte{0x1},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000 + 3 + 10 + 1), // DefaultInstanceCost len("foo") + 1 * DefaultPerAttributeCost + len(data)
|
||||
},
|
||||
"subcall response with events - unpinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: "myKey", Value: "myData"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000 + 3 + 10), // DefaultInstanceCost + len("foo") + 1 * DefaultPerAttributeCost
|
||||
},
|
||||
"subcall response with events exceeds free tier- unpinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Ok: &wasmvmtypes.SubcallResponse{
|
||||
Events: []wasmvmtypes.Event{
|
||||
{Type: "foo", Attributes: []wasmvmtypes.EventAttribute{{Key: strings.Repeat("x", DefaultEventAttributeDataFreeTier), Value: "myData"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000 + 3 + 10 + 6), // DefaultInstanceCost + len("foo") + 1 * DefaultPerAttributeCost + len("myData")
|
||||
},
|
||||
"subcall response error - unpinned": {
|
||||
src: wasmvmtypes.Reply{
|
||||
Result: wasmvmtypes.SubcallResult{
|
||||
Err: "foo",
|
||||
},
|
||||
},
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
exp: sdk.Gas(40_000 + 3), // DefaultInstanceCost + len("foo")
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
NewWasmGasRegister(spec.srcConfig).ReplyCosts(spec.pinned, spec.src)
|
||||
})
|
||||
return
|
||||
}
|
||||
gotGas := NewWasmGasRegister(spec.srcConfig).ReplyCosts(spec.pinned, spec.src)
|
||||
assert.Equal(t, spec.exp, gotGas)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToWasmVMGasConversion(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
src storetypes.Gas
|
||||
srcConfig WasmGasRegisterConfig
|
||||
exp uint64
|
||||
expPanic bool
|
||||
}{
|
||||
"0": {
|
||||
src: 0,
|
||||
exp: 0,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
},
|
||||
"max": {
|
||||
srcConfig: WasmGasRegisterConfig{
|
||||
GasMultiplier: 1,
|
||||
},
|
||||
src: math.MaxUint64,
|
||||
exp: math.MaxUint64,
|
||||
},
|
||||
"overflow": {
|
||||
srcConfig: WasmGasRegisterConfig{
|
||||
GasMultiplier: 2,
|
||||
},
|
||||
src: math.MaxUint64,
|
||||
expPanic: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
r := NewWasmGasRegister(spec.srcConfig)
|
||||
_ = r.ToWasmVMGas(spec.src)
|
||||
})
|
||||
return
|
||||
}
|
||||
r := NewWasmGasRegister(spec.srcConfig)
|
||||
got := r.ToWasmVMGas(spec.src)
|
||||
assert.Equal(t, spec.exp, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestFromWasmVMGasConversion(t *testing.T) {
|
||||
specs := map[string]struct {
|
||||
src uint64
|
||||
exp storetypes.Gas
|
||||
srcConfig WasmGasRegisterConfig
|
||||
expPanic bool
|
||||
}{
|
||||
"0": {
|
||||
src: 0,
|
||||
exp: 0,
|
||||
srcConfig: DefaultGasRegisterConfig(),
|
||||
},
|
||||
"max": {
|
||||
srcConfig: WasmGasRegisterConfig{
|
||||
GasMultiplier: 1,
|
||||
},
|
||||
src: math.MaxUint64,
|
||||
exp: math.MaxUint64,
|
||||
},
|
||||
"missconfigured": {
|
||||
srcConfig: WasmGasRegisterConfig{
|
||||
GasMultiplier: 0,
|
||||
},
|
||||
src: 1,
|
||||
expPanic: true,
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if spec.expPanic {
|
||||
assert.Panics(t, func() {
|
||||
r := NewWasmGasRegister(spec.srcConfig)
|
||||
_ = r.FromWasmVMGas(spec.src)
|
||||
})
|
||||
return
|
||||
}
|
||||
r := NewWasmGasRegister(spec.srcConfig)
|
||||
got := r.FromWasmVMGas(spec.src)
|
||||
assert.Equal(t, spec.exp, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -20,21 +20,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// DefaultGasMultiplier is how many cosmwasm gas points = 1 sdk gas point
|
||||
// SDK reference costs can be found here: https://github.com/cosmos/cosmos-sdk/blob/02c6c9fafd58da88550ab4d7d494724a477c8a68/store/types/gas.go#L153-L164
|
||||
// A write at ~3000 gas and ~200us = 10 gas per us (microsecond) cpu/io
|
||||
// Rough timing have 88k gas at 90us, which is equal to 1k sdk gas... (one read)
|
||||
//
|
||||
// Please note that all gas prices returned to the wasmer engine should have this multiplied
|
||||
const DefaultGasMultiplier uint64 = 100
|
||||
|
||||
// DefaultInstanceCost is how much SDK gas we charge each time we load a WASM instance.
|
||||
// Creating a new instance is costly, and this helps put a recursion limit to contracts calling contracts.
|
||||
const DefaultInstanceCost uint64 = 40_000
|
||||
|
||||
// DefaultCompileCost is how much SDK gas we charge *per byte* for compiling WASM code.
|
||||
const DefaultCompileCost uint64 = 2
|
||||
|
||||
// contractMemoryLimit is the memory limit of each contract execution (in MiB)
|
||||
// constant value so all nodes run with the same limit.
|
||||
const contractMemoryLimit = 32
|
||||
|
@ -83,9 +68,7 @@ type Keeper struct {
|
|||
// queryGasLimit is the max wasmvm gas that can be spent on executing a query with a contract
|
||||
queryGasLimit uint64
|
||||
paramSpace paramtypes.Subspace
|
||||
instanceCost uint64
|
||||
compileCost uint64
|
||||
gasMultiplier uint64
|
||||
gasRegister GasRegister
|
||||
}
|
||||
|
||||
// NewKeeper creates a new contract Keeper instance
|
||||
|
@ -129,9 +112,7 @@ func NewKeeper(
|
|||
messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource),
|
||||
queryGasLimit: wasmConfig.SmartQueryGasLimit,
|
||||
paramSpace: paramSpace,
|
||||
instanceCost: DefaultInstanceCost,
|
||||
compileCost: DefaultCompileCost,
|
||||
gasMultiplier: DefaultGasMultiplier,
|
||||
gasRegister: NewDefaultWasmGasRegister(),
|
||||
}
|
||||
|
||||
keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, queryRouter, keeper)
|
||||
|
@ -180,7 +161,7 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
|
|||
if err != nil {
|
||||
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
|
||||
}
|
||||
ctx.GasMeter().ConsumeGas(k.compileCost*uint64(len(wasmCode)), "Compiling WASM Bytecode")
|
||||
ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling WASM Bytecode")
|
||||
|
||||
codeHash, err := k.wasmVM.Create(wasmCode)
|
||||
if err != nil {
|
||||
|
@ -227,9 +208,9 @@ func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeIn
|
|||
|
||||
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) {
|
||||
defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "instantiate")
|
||||
if !k.IsPinnedCode(ctx, codeID) {
|
||||
ctx.GasMeter().ConsumeGas(k.instanceCost, "Loading CosmWasm module: instantiate")
|
||||
}
|
||||
|
||||
instanceCosts := k.gasRegister.NewContractInstanceCosts(k.IsPinnedCode(ctx, codeID), len(initMsg))
|
||||
ctx.GasMeter().ConsumeGas(instanceCosts, "Loading CosmWasm module: instantiate")
|
||||
|
||||
// create contract address
|
||||
contractAddress := k.generateContractAddress(ctx, codeID)
|
||||
|
@ -277,17 +258,13 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
|
|||
querier := k.newQueryHandler(ctx, contractAddress)
|
||||
|
||||
// instantiate wasm contract
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, err := k.wasmVM.Instantiate(codeInfo.CodeHash, env, info, initMsg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if err != nil {
|
||||
return contractAddress, nil, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddress)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
// persist instance first
|
||||
createdAt := types.NewAbsoluteTxPosition(ctx)
|
||||
contractInfo := types.NewContractInfo(codeID, creator, admin, label, createdAt)
|
||||
|
@ -313,7 +290,7 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
|
|||
k.storeContractInfo(ctx, contractAddress, &contractInfo)
|
||||
|
||||
// dispatch submessages then messages
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res)
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Data)
|
||||
if err != nil {
|
||||
return nil, nil, sdkerrors.Wrap(err, "dispatch")
|
||||
}
|
||||
|
@ -329,9 +306,8 @@ func (k Keeper) execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if !k.IsPinnedCode(ctx, contractInfo.CodeID) {
|
||||
ctx.GasMeter().ConsumeGas(k.instanceCost, "Loading CosmWasm module: execute")
|
||||
}
|
||||
executeCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(msg))
|
||||
ctx.GasMeter().ConsumeGas(executeCosts, "Loading CosmWasm module: execute")
|
||||
|
||||
// add more funds
|
||||
if !coins.IsZero() {
|
||||
|
@ -345,19 +321,15 @@ func (k Keeper) execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
|
||||
// prepare querier
|
||||
querier := k.newQueryHandler(ctx, contractAddress)
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.Execute(codeInfo.CodeHash, env, info, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(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
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res)
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Data)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "dispatch")
|
||||
}
|
||||
|
@ -366,9 +338,8 @@ func (k Keeper) execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
|
||||
func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte, authZ AuthorizationPolicy) ([]byte, error) {
|
||||
defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "migrate")
|
||||
if !k.IsPinnedCode(ctx, newCodeID) {
|
||||
ctx.GasMeter().ConsumeGas(k.instanceCost, "Loading CosmWasm module: migrate")
|
||||
}
|
||||
migrateSetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, newCodeID), len(msg))
|
||||
ctx.GasMeter().ConsumeGas(migrateSetupCosts, "Loading CosmWasm module: migrate")
|
||||
|
||||
contractInfo := k.GetContractInfo(ctx, contractAddress)
|
||||
if contractInfo == nil {
|
||||
|
@ -406,17 +377,13 @@ func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
|
||||
prefixStoreKey := types.GetContractStorePrefix(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, err := k.wasmVM.Migrate(newCodeInfo.CodeHash, env, msg, &prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(types.ErrMigrationFailed, err.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract migration itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddress)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
// delete old secondary index entry
|
||||
k.removeFromContractCodeSecondaryIndex(ctx, contractAddress, k.getLastContractHistoryEntry(ctx, contractAddress))
|
||||
// persist migration updates
|
||||
|
@ -426,7 +393,7 @@ func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
|||
k.storeContractInfo(ctx, contractAddress, contractInfo)
|
||||
|
||||
// dispatch submessages then messages
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res)
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Data)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "dispatch")
|
||||
}
|
||||
|
@ -443,27 +410,22 @@ func (k Keeper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if !k.IsPinnedCode(ctx, contractInfo.CodeID) {
|
||||
ctx.GasMeter().ConsumeGas(k.instanceCost, "Loading CosmWasm module: sudo")
|
||||
}
|
||||
sudoSetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(msg))
|
||||
ctx.GasMeter().ConsumeGas(sudoSetupCosts, "Loading CosmWasm module: sudo")
|
||||
|
||||
env := types.NewEnv(ctx, contractAddress)
|
||||
|
||||
// prepare querier
|
||||
querier := k.newQueryHandler(ctx, contractAddress)
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.Sudo(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(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
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res)
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Data)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "dispatch")
|
||||
}
|
||||
|
@ -478,10 +440,8 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply was
|
|||
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(k.instanceCost, "Loading CosmWasm module: reply")
|
||||
}
|
||||
replyCosts := k.gasRegister.ReplyCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), reply)
|
||||
ctx.GasMeter().ConsumeGas(replyCosts, "Loading CosmWasm module: reply")
|
||||
|
||||
env := types.NewEnv(ctx, contractAddress)
|
||||
|
||||
|
@ -490,19 +450,15 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply was
|
|||
Ctx: ctx,
|
||||
Plugins: k.wasmVMQueryHandler,
|
||||
}
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.Reply(codeInfo.CodeHash, env, reply, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(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
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res)
|
||||
data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Data)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "dispatch")
|
||||
}
|
||||
|
@ -592,16 +548,16 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !k.IsPinnedCode(ctx, contractInfo.CodeID) {
|
||||
ctx.GasMeter().ConsumeGas(k.instanceCost, "Loading CosmWasm module: query")
|
||||
}
|
||||
|
||||
smartQuerySetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(req))
|
||||
ctx.GasMeter().ConsumeGas(smartQuerySetupCosts, "Loading CosmWasm module: query")
|
||||
|
||||
// prepare querier
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
env := types.NewEnv(ctx, contractAddr)
|
||||
queryResult, gasUsed, qErr := k.wasmVM.Query(codeInfo.CodeHash, env, req, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), k.gasForContract(ctx))
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
queryResult, gasUsed, qErr := k.wasmVM.Query(codeInfo.CodeHash, env, req, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), k.runtimeGasForContract(ctx))
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if qErr != nil {
|
||||
return nil, sdkerrors.Wrap(types.ErrQueryFailed, qErr.Error())
|
||||
}
|
||||
|
@ -802,22 +758,37 @@ func (k Keeper) setContractInfoExtension(ctx sdk.Context, contractAddr sdk.AccAd
|
|||
return nil
|
||||
}
|
||||
|
||||
// handleContractResponse processes the contract response
|
||||
func (k *Keeper) handleContractResponse(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, res *wasmvmtypes.Response) ([]byte, error) {
|
||||
return k.wasmVMResponseHandler.Handle(ctx, contractAddr, ibcPort, res.Submessages, res.Messages, res.Data)
|
||||
// handleContractResponse processes the contract response data by emitting events and sending sub-/messages.
|
||||
func (k *Keeper) handleContractResponse(
|
||||
ctx sdk.Context,
|
||||
contractAddr sdk.AccAddress,
|
||||
ibcPort string,
|
||||
subMsg []wasmvmtypes.SubMsg,
|
||||
msgs []wasmvmtypes.CosmosMsg,
|
||||
attrs []wasmvmtypes.EventAttribute,
|
||||
data []byte,
|
||||
) ([]byte, error) {
|
||||
attributeGasCost := k.gasRegister.EventCosts(attrs)
|
||||
ctx.GasMeter().ConsumeGas(attributeGasCost, "Custom contract event attributes")
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(attrs, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
return k.wasmVMResponseHandler.Handle(ctx, contractAddr, ibcPort, subMsg, msgs, data)
|
||||
}
|
||||
|
||||
func (k Keeper) gasForContract(ctx sdk.Context) uint64 {
|
||||
func (k Keeper) runtimeGasForContract(ctx sdk.Context) uint64 {
|
||||
meter := ctx.GasMeter()
|
||||
if meter.IsOutOfGas() {
|
||||
return 0
|
||||
}
|
||||
remaining := (meter.Limit() - meter.GasConsumedToLimit()) * k.gasMultiplier
|
||||
return remaining
|
||||
if meter.Limit() == 0 { // infinite gas meter with limit=0 and not out of gas
|
||||
return math.MaxUint64
|
||||
}
|
||||
return k.gasRegister.ToWasmVMGas(meter.Limit() - meter.GasConsumedToLimit())
|
||||
}
|
||||
|
||||
func (k Keeper) consumeGas(ctx sdk.Context, gas uint64) {
|
||||
consumed := gas / k.gasMultiplier
|
||||
func (k Keeper) consumeRuntimeGas(ctx sdk.Context, gas uint64) {
|
||||
consumed := k.gasRegister.FromWasmVMGas(gas)
|
||||
ctx.GasMeter().ConsumeGas(consumed, "wasm contract")
|
||||
// throw OutOfGas error if we ran out (got exactly to zero due to better limit enforcing)
|
||||
if ctx.GasMeter().IsOutOfGas() {
|
||||
|
@ -903,7 +874,7 @@ func (k Keeper) importContract(ctx sdk.Context, contractAddr sdk.AccAddress, c *
|
|||
}
|
||||
|
||||
func (k Keeper) newQueryHandler(ctx sdk.Context, contractAddress sdk.AccAddress) QueryHandler {
|
||||
return NewQueryHandler(ctx, k.wasmVMQueryHandler, contractAddress, k.gasMultiplier)
|
||||
return NewQueryHandler(ctx, k.wasmVMQueryHandler, contractAddress, k.gasRegister)
|
||||
}
|
||||
|
||||
func addrFromUint64(id uint64) sdk.AccAddress {
|
||||
|
@ -916,21 +887,21 @@ func addrFromUint64(id uint64) sdk.AccAddress {
|
|||
// MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier
|
||||
type MultipliedGasMeter struct {
|
||||
originalMeter sdk.GasMeter
|
||||
gasMultiplier uint64
|
||||
GasRegister GasRegister
|
||||
}
|
||||
|
||||
func NewMultipliedGasMeter(originalMeter sdk.GasMeter, gasMultiplier uint64) MultipliedGasMeter {
|
||||
return MultipliedGasMeter{originalMeter: originalMeter, gasMultiplier: gasMultiplier}
|
||||
func NewMultipliedGasMeter(originalMeter sdk.GasMeter, gr GasRegister) MultipliedGasMeter {
|
||||
return MultipliedGasMeter{originalMeter: originalMeter, GasRegister: gr}
|
||||
}
|
||||
|
||||
var _ wasmvm.GasMeter = MultipliedGasMeter{}
|
||||
|
||||
func (m MultipliedGasMeter) GasConsumed() sdk.Gas {
|
||||
return m.originalMeter.GasConsumed() * m.gasMultiplier
|
||||
return m.GasRegister.ToWasmVMGas(m.originalMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (k Keeper) gasMeter(ctx sdk.Context) MultipliedGasMeter {
|
||||
return NewMultipliedGasMeter(ctx.GasMeter(), k.gasMultiplier)
|
||||
return NewMultipliedGasMeter(ctx.GasMeter(), k.gasRegister)
|
||||
}
|
||||
|
||||
// Logger returns a module-specific logger.
|
||||
|
|
|
@ -284,7 +284,7 @@ func TestInstantiate(t *testing.T) {
|
|||
|
||||
gasAfter := ctx.GasMeter().GasConsumed()
|
||||
if types.EnableGasVerification {
|
||||
require.Equal(t, uint64(0x122a0), gasAfter-gasBefore)
|
||||
require.Equal(t, uint64(0x12324), gasAfter-gasBefore)
|
||||
}
|
||||
|
||||
// ensure it is stored properly
|
||||
|
@ -517,7 +517,7 @@ func TestExecute(t *testing.T) {
|
|||
// make sure gas is properly deducted from ctx
|
||||
gasAfter := ctx.GasMeter().GasConsumed()
|
||||
if types.EnableGasVerification {
|
||||
require.Equal(t, uint64(0x12917), gasAfter-gasBefore)
|
||||
require.Equal(t, uint64(0x12939), gasAfter-gasBefore)
|
||||
}
|
||||
// ensure bob now exists and got both payments released
|
||||
bobAcct = accKeeper.GetAccount(ctx, bob)
|
||||
|
|
|
@ -66,7 +66,7 @@ func (m msgServer) InstantiateContract(goCtx context.Context, msg *types.MsgInst
|
|||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeySigner, msg.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyCodeID, fmt.Sprintf("%d", msg.CodeID)),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, contractAddr.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddr.String()),
|
||||
sdk.NewAttribute(types.AttributeResultDataHex, hex.EncodeToString(data)),
|
||||
))
|
||||
|
||||
|
@ -96,7 +96,7 @@ func (m msgServer) ExecuteContract(goCtx context.Context, msg *types.MsgExecuteC
|
|||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeySigner, msg.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeResultDataHex, hex.EncodeToString(data)),
|
||||
))
|
||||
|
||||
|
@ -126,7 +126,7 @@ func (m msgServer) MigrateContract(goCtx context.Context, msg *types.MsgMigrateC
|
|||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeySigner, msg.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyCodeID, fmt.Sprintf("%d", msg.CodeID)),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeResultDataHex, hex.EncodeToString(data)),
|
||||
))
|
||||
|
||||
|
@ -158,7 +158,7 @@ func (m msgServer) UpdateAdmin(goCtx context.Context, msg *types.MsgUpdateAdmin)
|
|||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeySigner, msg.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, msg.Contract),
|
||||
))
|
||||
|
||||
return &types.MsgUpdateAdminResponse{}, nil
|
||||
|
@ -183,7 +183,7 @@ func (m msgServer) ClearAdmin(goCtx context.Context, msg *types.MsgClearAdmin) (
|
|||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeySigner, msg.Sender),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, msg.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, msg.Contract),
|
||||
))
|
||||
|
||||
return &types.MsgClearAdminResponse{}, nil
|
||||
|
|
|
@ -82,18 +82,12 @@ func WithVMCacheMetrics(r prometheus.Registerer) Option {
|
|||
})
|
||||
}
|
||||
|
||||
// WithCosts sets custom gas costs and multiplier.
|
||||
// See DefaultCompileCost, DefaultInstanceCost, DefaultGasMultiplier
|
||||
// Uses WithApiCosts with defaults and given multiplier.
|
||||
func WithCosts(compile, instance, multiplier uint64) Option {
|
||||
// WithGasRegister set a new gas register to implement custom gas costs.
|
||||
// When the "gas multiplier" for wasmvm gas convertion is modified inside the new register,
|
||||
// make sure to also use `WithApiCosts` option for non default values
|
||||
func WithGasRegister(x GasRegister) Option {
|
||||
return optsFn(func(k *Keeper) {
|
||||
k.compileCost = compile
|
||||
k.instanceCost = instance
|
||||
k.gasMultiplier = multiplier
|
||||
WithApiCosts(
|
||||
DefaultGasCostHumanAddress*multiplier,
|
||||
DefaultGasCostCanonicalAddress*multiplier,
|
||||
).apply(k)
|
||||
k.gasRegister = x
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -41,14 +41,9 @@ func TestConstructorOptions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"costs": {
|
||||
srcOpt: WithCosts(1, 2, 3),
|
||||
srcOpt: WithGasRegister(&wasmtesting.MockGasRegister{}),
|
||||
verify: func(t *testing.T, k Keeper) {
|
||||
t.Cleanup(setApiDefaults)
|
||||
assert.Equal(t, uint64(1), k.compileCost)
|
||||
assert.Equal(t, uint64(2), k.instanceCost)
|
||||
assert.Equal(t, uint64(3), k.gasMultiplier)
|
||||
assert.Equal(t, uint64(15), costHumanize)
|
||||
assert.Equal(t, uint64(12), costCanonical)
|
||||
assert.IsType(t, k.gasRegister, &wasmtesting.MockGasRegister{})
|
||||
},
|
||||
},
|
||||
"api costs": {
|
||||
|
|
|
@ -94,7 +94,7 @@ func handleInstantiateProposal(ctx sdk.Context, k types.ContractOpsKeeper, p typ
|
|||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyCodeID, fmt.Sprintf("%d", p.CodeID)),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, contractAddr.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddr.String()),
|
||||
sdk.NewAttribute(types.AttributeResultDataHex, hex.EncodeToString(data)),
|
||||
)
|
||||
ctx.EventManager().EmitEvent(ourEvent)
|
||||
|
@ -123,7 +123,7 @@ func handleMigrateProposal(ctx sdk.Context, k types.ContractOpsKeeper, p types.M
|
|||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyCodeID, fmt.Sprintf("%d", p.CodeID)),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, p.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, p.Contract),
|
||||
sdk.NewAttribute(types.AttributeResultDataHex, hex.EncodeToString(data)),
|
||||
)
|
||||
ctx.EventManager().EmitEvent(ourEvent)
|
||||
|
@ -150,7 +150,7 @@ func handleUpdateAdminProposal(ctx sdk.Context, k types.ContractOpsKeeper, p typ
|
|||
ourEvent := sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, p.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, p.Contract),
|
||||
)
|
||||
ctx.EventManager().EmitEvent(ourEvent)
|
||||
return nil
|
||||
|
@ -171,7 +171,7 @@ func handleClearAdminProposal(ctx sdk.Context, k types.ContractOpsKeeper, p type
|
|||
ourEvent := sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
|
||||
sdk.NewAttribute(types.AttributeKeyContract, p.Contract),
|
||||
sdk.NewAttribute(types.AttributeKeyContractAddr, p.Contract),
|
||||
)
|
||||
ctx.EventManager().EmitEvent(ourEvent)
|
||||
return nil
|
||||
|
|
|
@ -15,18 +15,18 @@ import (
|
|||
)
|
||||
|
||||
type QueryHandler struct {
|
||||
Ctx sdk.Context
|
||||
Plugins WasmVMQueryHandler
|
||||
Caller sdk.AccAddress
|
||||
GasMultiplier uint64
|
||||
Ctx sdk.Context
|
||||
Plugins WasmVMQueryHandler
|
||||
Caller sdk.AccAddress
|
||||
gasRegister GasRegister
|
||||
}
|
||||
|
||||
func NewQueryHandler(ctx sdk.Context, vmQueryHandler WasmVMQueryHandler, caller sdk.AccAddress, gasMultiplier uint64) QueryHandler {
|
||||
func NewQueryHandler(ctx sdk.Context, vmQueryHandler WasmVMQueryHandler, caller sdk.AccAddress, gasRegister GasRegister) QueryHandler {
|
||||
return QueryHandler{
|
||||
Ctx: ctx,
|
||||
Plugins: vmQueryHandler,
|
||||
Caller: caller,
|
||||
GasMultiplier: gasMultiplier,
|
||||
Ctx: ctx,
|
||||
Plugins: vmQueryHandler,
|
||||
Caller: caller,
|
||||
gasRegister: gasRegister,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ var _ wasmvmtypes.Querier = QueryHandler{}
|
|||
|
||||
func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) {
|
||||
// set a limit for a subctx
|
||||
sdkGas := gasLimit / q.GasMultiplier
|
||||
sdkGas := q.gasRegister.FromWasmVMGas(gasLimit)
|
||||
subctx := q.Ctx.WithGasMeter(sdk.NewGasMeter(sdkGas))
|
||||
|
||||
// make sure we charge the higher level context even on panic
|
||||
|
|
|
@ -57,12 +57,12 @@ func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.Acc
|
|||
|
||||
func TestGasCostOnQuery(t *testing.T) {
|
||||
const (
|
||||
GasNoWork uint64 = 44_072
|
||||
GasNoWork uint64 = 44_163
|
||||
// Note: about 100 SDK gas (10k wasmer gas) for each round of sha256
|
||||
GasWork50 uint64 = 49_764 // this is a little shy of 50k gas - to keep an eye on the limit
|
||||
GasWork50 uint64 = 49_856 // this is a little shy of 50k gas - to keep an eye on the limit
|
||||
|
||||
GasReturnUnhashed uint64 = 283
|
||||
GasReturnHashed uint64 = 257
|
||||
GasReturnUnhashed uint64 = 224
|
||||
GasReturnHashed uint64 = 198
|
||||
)
|
||||
|
||||
cases := map[string]struct {
|
||||
|
@ -221,9 +221,9 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
|
|||
|
||||
const (
|
||||
// Note: about 100 SDK gas (10k wasmer gas) for each round of sha256
|
||||
GasWork2k uint64 = 273_567 // = InstanceCost + x // we have 6x gas used in cpu than in the instance
|
||||
GasWork2k uint64 = 273_661 // = NewContractInstanceCosts + x // we have 6x gas used in cpu than in the instance
|
||||
// This is overhead for calling into a sub-contract
|
||||
GasReturnHashed uint64 = 262
|
||||
GasReturnHashed uint64 = 203
|
||||
)
|
||||
|
||||
cases := map[string]struct {
|
||||
|
|
|
@ -29,9 +29,9 @@ func (k Keeper) OnOpenChannel(
|
|||
env := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, channel, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if execErr != nil {
|
||||
return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
|
@ -60,21 +60,14 @@ func (k Keeper) OnConnectChannel(
|
|||
env := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, channel, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if execErr != nil {
|
||||
return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
if _, err := k.wasmVMResponseHandler.Handle(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res)
|
||||
}
|
||||
|
||||
// OnCloseChannel calls the contract to let it know the IBC channel is closed.
|
||||
|
@ -98,21 +91,14 @@ func (k Keeper) OnCloseChannel(
|
|||
params := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, channel, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if execErr != nil {
|
||||
return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
if _, err := k.wasmVMResponseHandler.Handle(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res)
|
||||
}
|
||||
|
||||
// OnRecvPacket calls the contract to process the incoming IBC packet. The contract fully owns the data processing and
|
||||
|
@ -135,17 +121,14 @@ func (k Keeper) OnRecvPacket(
|
|||
env := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, packet, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(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, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
return k.wasmVMResponseHandler.Handle(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Acknowledgement)
|
||||
return k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, res.Attributes, res.Acknowledgement)
|
||||
}
|
||||
|
||||
// OnAckPacket calls the contract to handle the "acknowledgement" data which can contain success or failure of a packet
|
||||
|
@ -169,21 +152,13 @@ func (k Keeper) OnAckPacket(
|
|||
env := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, acknowledgement, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if execErr != nil {
|
||||
return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
if _, err := k.wasmVMResponseHandler.Handle(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res)
|
||||
}
|
||||
|
||||
// OnTimeoutPacket calls the contract to let it know the packet was never received on the destination chain within
|
||||
|
@ -204,19 +179,17 @@ func (k Keeper) OnTimeoutPacket(
|
|||
env := types.NewEnv(ctx, contractAddr)
|
||||
querier := k.newQueryHandler(ctx, contractAddr)
|
||||
|
||||
gas := k.gasForContract(ctx)
|
||||
gas := k.runtimeGasForContract(ctx)
|
||||
res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, packet, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas)
|
||||
k.consumeGas(ctx, gasUsed)
|
||||
k.consumeRuntimeGas(ctx, gasUsed)
|
||||
if execErr != nil {
|
||||
return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
|
||||
// emit all events from this contract itself
|
||||
events := types.ParseEvents(res.Attributes, contractAddr)
|
||||
ctx.EventManager().EmitEvents(events)
|
||||
|
||||
if _, err := k.wasmVMResponseHandler.Handle(ctx, contractAddr, contractInfo.IBCPortID, res.Submessages, res.Messages, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res)
|
||||
}
|
||||
|
||||
func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *wasmvmtypes.IBCBasicResponse) error {
|
||||
_, err := k.handleContractResponse(ctx, addr, id, res.Submessages, res.Messages, res.Attributes, nil)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,24 +19,28 @@ func TestOnOpenChannel(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractErr error
|
||||
expGas uint64
|
||||
expErr bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractGas: myContractGas,
|
||||
expGas: myContractGas,
|
||||
},
|
||||
"consume max gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: math.MaxUint64 / DefaultGasMultiplier,
|
||||
expGas: math.MaxUint64 / DefaultGasMultiplier,
|
||||
},
|
||||
"consume gas on error": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractGas: myContractGas,
|
||||
contractErr: errors.New("test, ignore"),
|
||||
expErr: true,
|
||||
},
|
||||
|
@ -53,8 +57,7 @@ func TestOnOpenChannel(t *testing.T) {
|
|||
return spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
defer cancel()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
|
||||
// when
|
||||
|
@ -68,7 +71,7 @@ func TestOnOpenChannel(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -79,25 +82,26 @@ func TestOnConnectChannel(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractResp *wasmvmtypes.IBCBasicResponse
|
||||
contractErr error
|
||||
overwriteMessenger *wasmtesting.MockMessageHandler
|
||||
expContractGas sdk.Gas
|
||||
expErr bool
|
||||
expContractEventAttrs int
|
||||
expNoEvents bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
},
|
||||
"consume gas on error, ignore events + messages": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -107,23 +111,23 @@ func TestOnConnectChannel(t *testing.T) {
|
|||
expNoEvents: true,
|
||||
},
|
||||
"dispatch contract messages on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 30,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
},
|
||||
},
|
||||
"emit contract events on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 40,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
},
|
||||
expContractEventAttrs: 1,
|
||||
},
|
||||
"messenger errors returned, events stored": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 50,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -143,12 +147,12 @@ func TestOnConnectChannel(t *testing.T) {
|
|||
myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"}
|
||||
m.IBCChannelConnectFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannel, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
|
||||
assert.Equal(t, channel, myChannel)
|
||||
return spec.contractResp, spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
return spec.contractResp, myContractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
defer cancel()
|
||||
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
||||
*messenger = *msger
|
||||
|
@ -175,7 +179,7 @@ func TestOnConnectChannel(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
// verify msgs dispatched
|
||||
assert.Equal(t, spec.contractResp.Messages, *capturedMsgs)
|
||||
assert.Len(t, events[0].Attributes, 1+spec.expContractEventAttrs)
|
||||
|
@ -189,25 +193,26 @@ func TestOnCloseChannel(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractResp *wasmvmtypes.IBCBasicResponse
|
||||
contractErr error
|
||||
overwriteMessenger *wasmtesting.MockMessageHandler
|
||||
expContractGas sdk.Gas
|
||||
expErr bool
|
||||
expContractEventAttrs int
|
||||
expNoEvents bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
},
|
||||
"consume gas on error, ignore events + messages": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -217,23 +222,23 @@ func TestOnCloseChannel(t *testing.T) {
|
|||
expNoEvents: true,
|
||||
},
|
||||
"dispatch contract messages on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 30,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
},
|
||||
},
|
||||
"emit contract events on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 40,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
},
|
||||
expContractEventAttrs: 1,
|
||||
},
|
||||
"messenger errors returned, events stored": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 50,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -253,11 +258,10 @@ func TestOnCloseChannel(t *testing.T) {
|
|||
myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"}
|
||||
m.IBCChannelCloseFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannel, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
|
||||
assert.Equal(t, channel, myChannel)
|
||||
return spec.contractResp, spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
return spec.contractResp, myContractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
defer cancel()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
||||
*messenger = *msger
|
||||
|
@ -285,7 +289,7 @@ func TestOnCloseChannel(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
// verify msgs dispatched
|
||||
assert.Equal(t, spec.contractResp.Messages, *capturedMsgs)
|
||||
require.Len(t, events, 1)
|
||||
|
@ -300,32 +304,33 @@ func TestOnRecvPacket(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractResp *wasmvmtypes.IBCReceiveResponse
|
||||
contractErr error
|
||||
overwriteMessenger *wasmtesting.MockMessageHandler
|
||||
expContractGas sdk.Gas
|
||||
expErr bool
|
||||
expContractEventAttrs int
|
||||
expNoEvents bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{
|
||||
Acknowledgement: []byte("myAck"),
|
||||
},
|
||||
},
|
||||
"can return empty ack": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{},
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{},
|
||||
},
|
||||
"consume gas on error, ignore events + messages": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{
|
||||
Acknowledgement: []byte("myAck"),
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}},
|
||||
|
@ -336,16 +341,16 @@ func TestOnRecvPacket(t *testing.T) {
|
|||
expNoEvents: true,
|
||||
},
|
||||
"dispatch contract messages on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 30,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{
|
||||
Acknowledgement: []byte("myAck"),
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
},
|
||||
},
|
||||
"emit contract events on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 40,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{
|
||||
Acknowledgement: []byte("myAck"),
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -353,8 +358,8 @@ func TestOnRecvPacket(t *testing.T) {
|
|||
expContractEventAttrs: 1,
|
||||
},
|
||||
"messenger errors returned, events stored": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 50,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCReceiveResponse{
|
||||
Acknowledgement: []byte("myAck"),
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
|
@ -376,11 +381,10 @@ func TestOnRecvPacket(t *testing.T) {
|
|||
|
||||
m.IBCPacketReceiveFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacket, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.IBCReceiveResponse, uint64, error) {
|
||||
assert.Equal(t, myPacket, packet)
|
||||
return spec.contractResp, spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
return spec.contractResp, myContractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
defer cancel()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
|
||||
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
||||
|
@ -411,7 +415,7 @@ func TestOnRecvPacket(t *testing.T) {
|
|||
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
// verify msgs dispatched
|
||||
assert.Equal(t, spec.contractResp.Messages, *capturedMsgs)
|
||||
require.Len(t, events, 1)
|
||||
|
@ -426,25 +430,26 @@ func TestOnAckPacket(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractResp *wasmvmtypes.IBCBasicResponse
|
||||
contractErr error
|
||||
overwriteMessenger *wasmtesting.MockMessageHandler
|
||||
expContractGas sdk.Gas
|
||||
expErr bool
|
||||
expContractEventAttrs int
|
||||
expNoEvents bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
},
|
||||
"consume gas on error, ignore events + messages": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -454,23 +459,23 @@ func TestOnAckPacket(t *testing.T) {
|
|||
expNoEvents: true,
|
||||
},
|
||||
"dispatch contract messages on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 30,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
},
|
||||
},
|
||||
"emit contract events on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 40,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
},
|
||||
expContractEventAttrs: 1,
|
||||
},
|
||||
"messenger errors returned, events stored": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 50,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -491,11 +496,10 @@ func TestOnAckPacket(t *testing.T) {
|
|||
myAck := wasmvmtypes.IBCAcknowledgement{Acknowledgement: []byte("myAck")}
|
||||
m.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCAcknowledgement, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
|
||||
assert.Equal(t, myAck, ack)
|
||||
return spec.contractResp, spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
return spec.contractResp, myContractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
defer cancel()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
||||
*messenger = *msger
|
||||
|
@ -523,7 +527,7 @@ func TestOnAckPacket(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
// verify msgs dispatched
|
||||
assert.Equal(t, spec.contractResp.Messages, *capturedMsgs)
|
||||
require.Len(t, events, 1)
|
||||
|
@ -538,25 +542,26 @@ func TestOnTimeoutPacket(t *testing.T) {
|
|||
var messenger = &wasmtesting.MockMessageHandler{}
|
||||
parentCtx, keepers := CreateTestInput(t, false, SupportedFeatures, WithMessageHandler(messenger))
|
||||
example := SeedNewContractInstance(t, parentCtx, keepers, &m)
|
||||
const myContractGas = 40
|
||||
|
||||
specs := map[string]struct {
|
||||
contractAddr sdk.AccAddress
|
||||
contractGas sdk.Gas
|
||||
contractResp *wasmvmtypes.IBCBasicResponse
|
||||
contractErr error
|
||||
overwriteMessenger *wasmtesting.MockMessageHandler
|
||||
expContractGas sdk.Gas
|
||||
expErr bool
|
||||
expContractEventAttrs int
|
||||
expNoEvents bool
|
||||
}{
|
||||
"consume contract gas": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{},
|
||||
},
|
||||
"consume gas on error, ignore events + messages": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 20,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -566,23 +571,23 @@ func TestOnTimeoutPacket(t *testing.T) {
|
|||
expNoEvents: true,
|
||||
},
|
||||
"dispatch contract messages on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 30,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
},
|
||||
},
|
||||
"emit contract events on success": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 40,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
},
|
||||
expContractEventAttrs: 1,
|
||||
},
|
||||
"messenger errors returned, events stored": {
|
||||
contractAddr: example.Contract,
|
||||
contractGas: 50,
|
||||
contractAddr: example.Contract,
|
||||
expContractGas: myContractGas + 10,
|
||||
contractResp: &wasmvmtypes.IBCBasicResponse{
|
||||
Messages: []wasmvmtypes.CosmosMsg{{Bank: &wasmvmtypes.BankMsg{}}, {Custom: json.RawMessage(`{"foo":"bar"}`)}},
|
||||
Attributes: []wasmvmtypes.EventAttribute{{Key: "Foo", Value: "Bar"}},
|
||||
|
@ -602,11 +607,10 @@ func TestOnTimeoutPacket(t *testing.T) {
|
|||
myPacket := wasmvmtypes.IBCPacket{Data: []byte("my test packet")}
|
||||
m.IBCPacketTimeoutFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacket, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
|
||||
assert.Equal(t, myPacket, packet)
|
||||
return spec.contractResp, spec.contractGas * DefaultGasMultiplier, spec.contractErr
|
||||
return spec.contractResp, myContractGas * DefaultGasMultiplier, spec.contractErr
|
||||
}
|
||||
|
||||
ctx, cancel := parentCtx.CacheContext()
|
||||
defer cancel()
|
||||
ctx, _ := parentCtx.CacheContext()
|
||||
before := ctx.GasMeter().GasConsumed()
|
||||
msger, capturedMsgs := wasmtesting.NewCapturingMessageHandler()
|
||||
*messenger = *msger
|
||||
|
@ -634,7 +638,7 @@ func TestOnTimeoutPacket(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
// verify gas consumed
|
||||
const storageCosts = sdk.Gas(0xa9d)
|
||||
assert.Equal(t, spec.contractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
assert.Equal(t, spec.expContractGas, ctx.GasMeter().GasConsumed()-before-storageCosts)
|
||||
// verify msgs dispatched
|
||||
assert.Equal(t, spec.contractResp.Messages, *capturedMsgs)
|
||||
require.Len(t, events, 1)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package wasmtesting
|
||||
|
||||
import (
|
||||
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// MockGasRegister mock that implements keeper.GasRegister
|
||||
type MockGasRegister struct {
|
||||
CompileCostFn func(byteLength int) sdk.Gas
|
||||
NewContractInstanceCostFn func(pinned bool, msgLen int) sdk.Gas
|
||||
InstantiateContractCostFn func(pinned bool, msgLen int) sdk.Gas
|
||||
ReplyCostFn func(pinned bool, reply wasmvmtypes.Reply) sdk.Gas
|
||||
EventCostsFn func(evts []wasmvmtypes.EventAttribute) sdk.Gas
|
||||
ToWasmVMGasFn func(source sdk.Gas) uint64
|
||||
FromWasmVMGasFn func(source uint64) sdk.Gas
|
||||
}
|
||||
|
||||
func (m MockGasRegister) NewContractInstanceCosts(pinned bool, msgLen int) sdk.Gas {
|
||||
if m.NewContractInstanceCostFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.NewContractInstanceCostFn(pinned, msgLen)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) CompileCosts(byteLength int) sdk.Gas {
|
||||
if m.CompileCostFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.CompileCostFn(byteLength)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas {
|
||||
if m.InstantiateContractCostFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.InstantiateContractCostFn(pinned, msgLen)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) sdk.Gas {
|
||||
if m.ReplyCostFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.ReplyCostFn(pinned, reply)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) EventCosts(evts []wasmvmtypes.EventAttribute) sdk.Gas {
|
||||
if m.EventCostsFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.EventCostsFn(evts)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) ToWasmVMGas(source sdk.Gas) uint64 {
|
||||
if m.ToWasmVMGasFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.ToWasmVMGasFn(source)
|
||||
}
|
||||
|
||||
func (m MockGasRegister) FromWasmVMGas(source uint64) sdk.Gas {
|
||||
if m.FromWasmVMGasFn == nil {
|
||||
panic("not expected to be called")
|
||||
}
|
||||
return m.FromWasmVMGasFn(source)
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package types
|
||||
|
||||
const (
|
||||
CustomEventType = "wasm"
|
||||
EventTypePinCode = "pin_code"
|
||||
EventTypeUnpinCode = "unpin_code"
|
||||
)
|
||||
const ( // event attributes
|
||||
AttributeKeyContract = "contract_address"
|
||||
AttributeKeyCodeID = "code_id"
|
||||
AttributeKeySigner = "signer"
|
||||
AttributeResultDataHex = "result"
|
||||
AttributeKeyContractAddr = "contract_address"
|
||||
AttributeKeyCodeID = "code_id"
|
||||
AttributeKeySigner = "signer"
|
||||
AttributeResultDataHex = "result"
|
||||
)
|
||||
|
|
|
@ -297,14 +297,10 @@ func NewWasmCoins(cosmosCoins sdk.Coins) (wasmCoins []wasmvmtypes.Coin) {
|
|||
return wasmCoins
|
||||
}
|
||||
|
||||
const CustomEventType = "wasm"
|
||||
const AttributeKeyContractAddr = "contract_address"
|
||||
|
||||
// ParseEvents converts wasm LogAttributes into an sdk.Events
|
||||
// ParseEvents converts wasm LogAttributes into an sdk.Events. Returns events and number of bytes for custom attributes
|
||||
func ParseEvents(wasmOutputAttrs []wasmvmtypes.EventAttribute, contractAddr sdk.AccAddress) sdk.Events {
|
||||
// we always tag with the contract address issuing this event
|
||||
attrs := []sdk.Attribute{sdk.NewAttribute(AttributeKeyContractAddr, contractAddr.String())}
|
||||
|
||||
// append attributes from wasm to the sdk.Event
|
||||
for _, l := range wasmOutputAttrs {
|
||||
// and reserve the contract_address key for our use (not contract)
|
||||
|
|
Loading…
Reference in New Issue