CosmWasm/wasmvm

Let StoreCode (formally Create) consume its gas cost internally

webmaster128 opened this issue · 0 comments

Right now a call to wasmVM.Create is free of charge. It is up to the caller to charge gas for the call. This is not ideal as Create should not make it easy to forget gas tracking and take responsibility for its own resource usage.

Wasmd charged Create gas here. In other places of wasmd the call is not charged, which is not a problem but you get the point:

$ git grep -C 5 "wasmVM.Create"
x/wasm/keeper/keeper.go-                        return 0, checksum, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/keeper.go-                }
x/wasm/keeper/keeper.go-        }
x/wasm/keeper/keeper.go-
x/wasm/keeper/keeper.go-        ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling wasm bytecode")
x/wasm/keeper/keeper.go:        checksum, err = k.wasmVM.Create(wasmCode)
x/wasm/keeper/keeper.go-        if err != nil {
x/wasm/keeper/keeper.go-                return 0, checksum, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/keeper.go-        }
x/wasm/keeper/keeper.go-        report, err := k.wasmVM.AnalyzeCode(checksum)
x/wasm/keeper/keeper.go-        if err != nil {
--
x/wasm/keeper/keeper.go-                wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
x/wasm/keeper/keeper.go-                if err != nil {
x/wasm/keeper/keeper.go-                        return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/keeper.go-                }
x/wasm/keeper/keeper.go-        }
x/wasm/keeper/keeper.go:        newCodeHash, err := k.wasmVM.Create(wasmCode)
x/wasm/keeper/keeper.go-        if err != nil {
x/wasm/keeper/keeper.go-                return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/keeper.go-        }
x/wasm/keeper/keeper.go-        if !bytes.Equal(codeInfo.CodeHash, newCodeHash) {
x/wasm/keeper/keeper.go-                return sdkerrors.Wrap(types.ErrInvalid, "code hashes not same")
--
x/wasm/keeper/snapshotter.go-   if err != nil {
x/wasm/keeper/snapshotter.go-           return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/snapshotter.go-   }
x/wasm/keeper/snapshotter.go-
x/wasm/keeper/snapshotter.go-   // FIXME: check which codeIDs the checksum matches??
x/wasm/keeper/snapshotter.go:   _, err = k.wasmVM.Create(wasmCode)
x/wasm/keeper/snapshotter.go-   if err != nil {
x/wasm/keeper/snapshotter.go-           return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
x/wasm/keeper/snapshotter.go-   }
x/wasm/keeper/snapshotter.go-   return nil
x/wasm/keeper/snapshotter.go-}

Other functions have gas metering and track their gas usage internally, maybe given cost configuration like deserialization cost, like here:

wasmvm/lib.go

Lines 161 to 201 in 825dcea

func (vm *VM) Execute(
checksum Checksum,
env types.Env,
info types.MessageInfo,
executeMsg []byte,
store KVStore,
goapi GoAPI,
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) (*types.Response, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
return nil, 0, err
}
infoBin, err := json.Marshal(info)
if err != nil {
return nil, 0, err
}
data, gasUsed, err := api.Execute(vm.cache, checksum, envBin, infoBin, executeMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
if err != nil {
return nil, gasUsed, err
}
gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor()
if gasLimit < gasForDeserialization+gasUsed {
return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data))
}
gasUsed += gasForDeserialization
var result types.ContractResult
err = json.Unmarshal(data, &result)
if err != nil {
return nil, gasUsed, err
}
if result.Err != "" {
return nil, gasUsed, fmt.Errorf("%s", result.Err)
}
return result.Ok, gasUsed, nil
}

This addresses this comment:

wasmvm/lib.go

Line 58 in 825dcea

// TODO: return gas cost? Add gas limit??? there is no metering here...