Skip to content

Commit

Permalink
Charge gas for custom event attributes and messages (Finschia#539)
Browse files Browse the repository at this point in the history
* Charge gas for custom event attributes

* Introduce gas register for gas costs

* Review feedback

* Tests and minor updates

* Godoc
  • Loading branch information
alpe authored and loloicci committed Jan 27, 2022
1 parent 06a4dd7 commit c92fd6d
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 257 deletions.
6 changes: 3 additions & 3 deletions x/wasm/keeper/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

type cosmwasmAPIImpl struct {
gasMultiplier uint64
gasMultiplier GasMultiplier
}

func (a cosmwasmAPIImpl) humanAddress(canon []byte) (string, uint64, error) {
gas := 5 * a.gasMultiplier
gas := a.gasMultiplier.FromWasmVMGas(5)
if len(canon) != sdk.BytesAddrLen {
//nolint:stylecheck
return "", gas, fmt.Errorf("expected %d byte address", sdk.BytesAddrLen)
Expand All @@ -23,7 +23,7 @@ func (a cosmwasmAPIImpl) humanAddress(canon []byte) (string, uint64, error) {

func (a cosmwasmAPIImpl) canonicalAddress(human string) ([]byte, uint64, error) {
bz, err := sdk.AccAddressToBytes(human)
return bz, 4 * a.gasMultiplier, err
return bz, a.gasMultiplier.ToWasmVMGas(4), err
}

func (k Keeper) cosmwasmAPI(ctx sdk.Context) wasmvm.GoAPI {
Expand Down
92 changes: 92 additions & 0 deletions x/wasm/keeper/gas_register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package keeper

import (
wasmvmtypes "github.com/line/wasmvm/types"
sdk "github.com/line/lbm-sdk/types"
)

const (
// 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
// EventCosts costs to persist an event
EventCosts(evts []wasmvmtypes.EventAttribute) sdk.Gas
}

// WasmGasRegisterConfig config type
type WasmGasRegisterConfig struct {
// 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{
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 {
return WasmGasRegister{
c: c,
}
}

// 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()
}
Loading

0 comments on commit c92fd6d

Please sign in to comment.