Skip to content

Commit

Permalink
Charge gas for unzip wasm code
Browse files Browse the repository at this point in the history
  • Loading branch information
alpe committed Jul 7, 2022
1 parent 3787f3d commit 368445c
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 40 deletions.
2 changes: 1 addition & 1 deletion app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func CheckBalance(t *testing.T, app *WasmApp, addr sdk.AccAddress, balances sdk.
require.True(t, balances.IsEqual(app.bankKeeper.GetAllBalances(ctxCheck, addr)))
}

const DefaultGas = 1200000
const DefaultGas = 1_500_000

// SignCheckDeliver checks a generated signed transaction and simulates a
// block commitment with the given transaction. A test assertion is made using
Expand Down
14 changes: 4 additions & 10 deletions x/wasm/ioutils/ioutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,12 @@ import (
"github.com/CosmWasm/wasmd/x/wasm/types"
)

// Uncompress returns gzip uncompressed content if input was gzip, or original src otherwise
func Uncompress(src []byte, limit uint64) ([]byte, error) {
switch n := uint64(len(src)); {
case n < 3:
return src, nil
case n > limit:
// Uncompress expects a valid gzip source to unpack or fails. See IsGzip
func Uncompress(gzipSrc []byte, limit uint64) ([]byte, error) {
if uint64(len(gzipSrc)) > limit {
return nil, types.ErrLimit
}
if !bytes.Equal(gzipIdent, src[0:3]) {
return src, nil
}
zr, err := gzip.NewReader(bytes.NewReader(src))
zr, err := gzip.NewReader(bytes.NewReader(gzipSrc))
if err != nil {
return nil, err
}
Expand Down
21 changes: 0 additions & 21 deletions x/wasm/ioutils/ioutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"io"
"io/ioutil"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -29,30 +28,10 @@ func TestUncompress(t *testing.T) {
expError error
expResult []byte
}{
"handle wasm uncompressed": {
src: wasmRaw,
expResult: wasmRaw,
},
"handle wasm compressed": {
src: wasmGzipped,
expResult: wasmRaw,
},
"handle nil slice": {
src: nil,
expResult: nil,
},
"handle short unidentified": {
src: []byte{0x1, 0x2},
expResult: []byte{0x1, 0x2},
},
"handle input slice exceeding limit": {
src: []byte(strings.Repeat("a", maxSize+1)),
expError: types.ErrLimit,
},
"handle input slice at limit": {
src: []byte(strings.Repeat("a", maxSize)),
expResult: []byte(strings.Repeat("a", maxSize)),
},
"handle gzip identifier only": {
src: gzipIdent,
expError: io.ErrUnexpectedEOF,
Expand Down
2 changes: 1 addition & 1 deletion x/wasm/ioutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var (

// IsGzip returns checks if the file contents are gzip compressed
func IsGzip(input []byte) bool {
return bytes.Equal(input[:3], gzipIdent)
return len(input) > 3 && bytes.Equal(gzipIdent, input[0:3])
}

// IsWasm checks if the file contents are of wasm binary
Expand Down
2 changes: 2 additions & 0 deletions x/wasm/ioutils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func TestIsGzip(t *testing.T) {

require.False(t, IsGzip(wasmCode))
require.False(t, IsGzip(someRandomStr))
require.False(t, IsGzip(nil))
require.False(t, IsGzip(gzipData[0:3]))
require.True(t, IsGzip(gzipData))
}

Expand Down
15 changes: 15 additions & 0 deletions x/wasm/keeper/gas_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const (
DefaultPerCustomEventCost uint64 = 20
// DefaultEventAttributeDataFreeTier number of bytes of total attribute data we do not charge.
DefaultEventAttributeDataFreeTier = 100
// DefaultPerByteUncompressCost is how much SDK gas we charge per source byte to unpack
DefaultPerByteUncompressCost uint64 = 2
)

// GasRegister abstract source for gas costs
Expand All @@ -60,6 +62,8 @@ type GasRegister interface {
NewContractInstanceCosts(pinned bool, msgLen int) sdk.Gas
// CompileCosts costs to persist and "compile" a new wasm contract
CompileCosts(byteLength int) sdk.Gas
// UncompressCosts costs to unpack a new wasm contract
UncompressCosts(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
Expand All @@ -78,6 +82,8 @@ type WasmGasRegisterConfig struct {
InstanceCost sdk.Gas
// CompileCosts costs to persist and "compile" a new wasm contract
CompileCost sdk.Gas
// UncompressCost costs per byte to unpack a contract
UncompressCost 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
Expand Down Expand Up @@ -107,6 +113,7 @@ func DefaultGasRegisterConfig() WasmGasRegisterConfig {
EventAttributeDataCost: DefaultEventAttributeDataCost,
EventAttributeDataFreeTier: DefaultEventAttributeDataFreeTier,
ContractMessageDataCost: DefaultContractMessageDataCost,
UncompressCost: DefaultPerByteUncompressCost,
}
}

Expand Down Expand Up @@ -143,6 +150,14 @@ func (g WasmGasRegister) CompileCosts(byteLength int) storetypes.Gas {
return g.c.CompileCost * uint64(byteLength)
}

// UncompressCosts costs to unpack a new wasm contract
func (g WasmGasRegister) UncompressCosts(byteLength int) sdk.Gas {
if byteLength < 0 {
panic(sdkerrors.Wrap(types.ErrInvalid, "negative length"))
}
return g.c.UncompressCost * uint64(byteLength)
}

// InstantiateContractCosts costs when interacting with a wasm contract
func (g WasmGasRegister) InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas {
if msgLen < 0 {
Expand Down
20 changes: 13 additions & 7 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,14 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "instantiate access must be subset of default upload access")
}

wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
if ioutils.IsGzip(wasmCode) {
ctx.GasMeter().ConsumeGas(k.gasRegister.UncompressCosts(len(wasmCode)), "Uncompress gzip bytecode")
wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
}
ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling WASM Bytecode")
ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling wasm bytecode")

checksum, err := k.wasmVM.Create(wasmCode)
if err != nil {
Expand Down Expand Up @@ -216,9 +219,12 @@ func (k Keeper) storeCodeInfo(ctx sdk.Context, codeID uint64, codeInfo types.Cod
}

func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo, wasmCode []byte) error {
wasmCode, err := ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
if ioutils.IsGzip(wasmCode) {
var err error
wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
}
newCodeHash, err := k.wasmVM.Create(wasmCode)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions x/wasm/keeper/snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ func (ws *WasmSnapshotter) Restore(
}

func restoreV1(ctx sdk.Context, k *Keeper, compressedCode []byte) error {
if !ioutils.IsGzip(compressedCode) {
return types.ErrInvalid.Wrap("not a gzip")
}
wasmCode, err := ioutils.Uncompress(compressedCode, uint64(types.MaxWasmSize))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
Expand Down
8 changes: 8 additions & 0 deletions x/wasm/keeper/wasmtesting/gas_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type MockGasRegister struct {
EventCostsFn func(evts []wasmvmtypes.EventAttribute) sdk.Gas
ToWasmVMGasFn func(source sdk.Gas) uint64
FromWasmVMGasFn func(source uint64) sdk.Gas
UncompressCostsFn func(byteLength int) sdk.Gas
}

func (m MockGasRegister) NewContractInstanceCosts(pinned bool, msgLen int) sdk.Gas {
Expand All @@ -30,6 +31,13 @@ func (m MockGasRegister) CompileCosts(byteLength int) sdk.Gas {
return m.CompileCostFn(byteLength)
}

func (m MockGasRegister) UncompressCosts(byteLength int) sdk.Gas {
if m.UncompressCostsFn == nil {
panic("not expected to be called")
}
return m.UncompressCostsFn(byteLength)
}

func (m MockGasRegister) InstantiateContractCosts(pinned bool, msgLen int) sdk.Gas {
if m.InstantiateContractCostFn == nil {
panic("not expected to be called")
Expand Down

0 comments on commit 368445c

Please sign in to comment.