Generalize "pinned" to "discount" for cases where contract is in memory

This commit is contained in:
Simon Warta
2024-01-26 17:18:28 +01:00
parent 796907e94a
commit 315bf5ca18
2 changed files with 37 additions and 18 deletions

View File

@@ -9,8 +9,8 @@ import (
// MockGasRegister mock that implements keeper.GasRegister
type MockGasRegister struct {
CompileCostFn func(byteLength int) storetypes.Gas
SetupContractCostFn func(pinned bool, msgLen int) storetypes.Gas
ReplyCostFn func(pinned bool, reply wasmvmtypes.Reply) storetypes.Gas
SetupContractCostFn func(discount bool, msgLen int) storetypes.Gas
ReplyCostFn func(discount bool, reply wasmvmtypes.Reply) storetypes.Gas
EventCostsFn func(evts []wasmvmtypes.EventAttribute) storetypes.Gas
ToWasmVMGasFn func(source storetypes.Gas) uint64
FromWasmVMGasFn func(source uint64) storetypes.Gas
@@ -31,18 +31,18 @@ func (m MockGasRegister) UncompressCosts(byteLength int) storetypes.Gas {
return m.UncompressCostsFn(byteLength)
}
func (m MockGasRegister) SetupContractCost(pinned bool, msgLen int) storetypes.Gas {
func (m MockGasRegister) SetupContractCost(discount bool, msgLen int) storetypes.Gas {
if m.SetupContractCostFn == nil {
panic("not expected to be called")
}
return m.SetupContractCostFn(pinned, msgLen)
return m.SetupContractCostFn(discount, msgLen)
}
func (m MockGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) storetypes.Gas {
func (m MockGasRegister) ReplyCosts(discount bool, reply wasmvmtypes.Reply) storetypes.Gas {
if m.ReplyCostFn == nil {
panic("not expected to be called")
}
return m.ReplyCostFn(pinned, reply)
return m.ReplyCostFn(discount, reply)
}
func (m MockGasRegister) EventCosts(evts []wasmvmtypes.EventAttribute, _ wasmvmtypes.Events) storetypes.Gas {

View File

@@ -35,6 +35,11 @@ const (
// Creating a new instance is costly, and this helps put a recursion limit to contracts calling contracts.
// Benchmarks and numbers were discussed in: https://github.com/CosmWasm/wasmd/pull/634#issuecomment-938056803
DefaultInstanceCost uint64 = 60_000
// DefaultInstanceCostDiscount is charged instead of DefaultInstanceCost for cases where
// we assume the contract is loaded from an in-memory cache.
// For a long time it was implicitly just 0 in those cases.
// Now we use something small that roughly reflects the 45µs startup time (30x cheaper than DefaultInstanceCost).
DefaultInstanceCostDiscount uint64 = 2_000
// DefaultCompileCost is how much SDK gas is charged *per byte* for compiling WASM code.
// Benchmarks and numbers were discussed in: https://github.com/CosmWasm/wasmd/pull/634#issuecomment-938056803
DefaultCompileCost uint64 = 3
@@ -75,9 +80,9 @@ type GasRegister interface {
UncompressCosts(byteLength int) storetypes.Gas
// SetupContractCost are charged when interacting with a Wasm contract, i.e. every time
// the contract is prepared for execution through any entry point (execute/instantiate/sudo/query/ibc_*/...).
SetupContractCost(pinned bool, msgLen int) storetypes.Gas
SetupContractCost(discount bool, msgLen int) storetypes.Gas
// ReplyCosts costs to to handle a message reply
ReplyCosts(pinned bool, reply wasmvmtypes.Reply) storetypes.Gas
ReplyCosts(discount bool, reply wasmvmtypes.Reply) storetypes.Gas
// EventCosts costs to persist an event
EventCosts(attrs []wasmvmtypes.EventAttribute, events wasmvmtypes.Events) storetypes.Gas
// ToWasmVMGas converts from Cosmos SDK gas units to [CosmWasm gas] (aka. wasmvm gas)
@@ -92,8 +97,16 @@ type GasRegister interface {
// WasmGasRegisterConfig config type
type WasmGasRegisterConfig struct {
// InstanceCost costs when interacting with a wasm contract
// InstanceCost are charged when interacting with a Wasm contract.
// "Instance" refers to the in-memory Instance of the Wasm runtime, not the contract address on chain.
// InstanceCost are part of a contract's setup cost.
InstanceCost storetypes.Gas
// InstanceCostDiscount is a discounted version of InstanceCost. It is charged whenever
// we can reasonably assume that a contract is in one of the in-memory caches. E.g.
// when the contract is pinned or we send a reply to a contract that was executed before.
// See also https://github.com/CosmWasm/wasmd/issues/1798 for more thinking around
// discount cases.
InstanceCostDiscount storetypes.Gas
// CompileCosts costs to persist and "compile" a new wasm contract
CompileCost storetypes.Gas
// UncompressCost costs per byte to unpack a contract
@@ -120,6 +133,7 @@ type WasmGasRegisterConfig struct {
func DefaultGasRegisterConfig() WasmGasRegisterConfig {
return WasmGasRegisterConfig{
InstanceCost: DefaultInstanceCost,
InstanceCostDiscount: DefaultInstanceCostDiscount,
CompileCost: DefaultCompileCost,
GasMultiplier: DefaultGasMultiplier,
EventPerAttributeCost: DefaultPerAttributeCost,
@@ -167,20 +181,25 @@ func (g WasmGasRegister) UncompressCosts(byteLength int) storetypes.Gas {
return g.c.UncompressCost.Mul(uint64(byteLength)).Floor()
}
// SetupContractCost costs when interacting with a wasm contract
func (g WasmGasRegister) SetupContractCost(pinned bool, msgLen int) storetypes.Gas {
// SetupContractCost costs when interacting with a wasm contract.
// Set discount to true in cases where you can reasonably assume the contract
// is loaded from an in-memory cache (e.g. pinned contracts or replys).
func (g WasmGasRegister) SetupContractCost(discount bool, msgLen int) storetypes.Gas {
if msgLen < 0 {
panic(errorsmod.Wrap(ErrInvalid, "negative length"))
}
dataCosts := storetypes.Gas(msgLen) * g.c.ContractMessageDataCost
if pinned {
return dataCosts
dataCost := storetypes.Gas(msgLen) * g.c.ContractMessageDataCost
if discount {
return g.c.InstanceCostDiscount + dataCost
} else {
return g.c.InstanceCost + dataCost
}
return g.c.InstanceCost + dataCosts
}
// ReplyCosts costs to to handle a message reply
func (g WasmGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) storetypes.Gas {
// ReplyCosts costs to to handle a message reply.
// Set discount to true in cases where you can reasonably assume the contract
// is loaded from an in-memory cache (e.g. pinned contracts or replys).
func (g WasmGasRegister) ReplyCosts(discount bool, reply wasmvmtypes.Reply) storetypes.Gas {
var eventGas storetypes.Gas
msgLen := len(reply.Result.Err)
if reply.Result.Ok != nil {
@@ -193,7 +212,7 @@ func (g WasmGasRegister) ReplyCosts(pinned bool, reply wasmvmtypes.Reply) storet
// apply free tier on the whole set not per event
eventGas += g.EventCosts(attrs, nil)
}
return eventGas + g.SetupContractCost(pinned, msgLen)
return eventGas + g.SetupContractCost(discount, msgLen)
}
// EventCosts costs to persist an event