diff --git a/gno.land/cmd/gnoland/testdata/addpkg.txtar b/gno.land/cmd/gnoland/testdata/addpkg.txtar index e7437552b50..071096cb49d 100644 --- a/gno.land/cmd/gnoland/testdata/addpkg.txtar +++ b/gno.land/cmd/gnoland/testdata/addpkg.txtar @@ -3,21 +3,35 @@ ## start a new node gnoland start -## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar -gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +## add hello.gno package located in $WORK directory as gno.land/r/hello +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 -## execute Render -gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +## compare AddPkg +cmp stdout stdout.addpkg.success -## compare render -stdout '("hello from foo" string)' -stdout 'OK!' -stdout 'GAS WANTED: 2000000' -stdout 'GAS USED: [0-9]+' --- bar.gno -- -package bar +## execute SayHello +gnokey maketx call -pkgpath gno.land/r/hello -func SayHello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 -func Render(path string) string { - return "hello from foo" + +## compare SayHello +cmp stdout stdout.call.success + +-- hello.gno -- +package hello + +func SayHello() string { + return "hello world!" } + + +-- stdout.addpkg.success -- + +OK! +GAS WANTED: 2000000 +GAS USED: 119829 +-- stdout.call.success -- +("hello world!" string) +OK! +GAS WANTED: 2000000 +GAS USED: 52801 diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index ee007c058a1..03222c7e672 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -66,6 +66,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { baseKey := store.NewStoreKey("base") // Create BaseApp. + // TODO: Add a consensus based min gas prices for the node, by default it does not check baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) baseApp.SetAppVersion("dev") @@ -139,7 +140,6 @@ func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger, } cfg.Logger = logger - return NewAppWithOptions(cfg) } diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go new file mode 100644 index 00000000000..75d13aa6c5d --- /dev/null +++ b/gno.land/pkg/sdk/vm/gas_test.go @@ -0,0 +1,177 @@ +package vm + +import ( + "testing" + + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/store" + "github.com/stretchr/testify/assert" +) + +// Gas for entire tx is consumed in both CheckTx and DeliverTx. +// Gas for executing VM tx (VM CPU and Store Access in bytes) is consumed in DeliverTx. +// Gas for balance checking, message size checking, and signature verification is consumed (deducted) in checkTx. + +// Insufficient gas for a successful message. + +func TestAddPkgDeliverTxInsuffGas(t *testing.T) { + success := true + ctx, tx, vmHandler := setupAddPkg(success) + + ctx = ctx.WithMode(sdk.RunTxModeDeliver) + simulate := false + tx.Fee.GasWanted = 3000 + gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted) + + var res sdk.Result + abort := false + + defer func() { + if r := recover(); r != nil { + switch r.(type) { + case store.OutOfGasException: + res.Error = sdk.ABCIError(std.ErrOutOfGas("")) + abort = true + default: + t.Errorf("should panic on OutOfGasException only") + } + assert.True(t, abort) + assert.False(t, res.IsOK()) + gasCheck := gctx.GasMeter().GasConsumed() + assert.Equal(t, int64(3231), gasCheck) + } else { + t.Errorf("should panic") + } + }() + msgs := tx.GetMsgs() + res = vmHandler.Process(gctx, msgs[0]) +} + +// Enough gas for a successful message. +func TestAddPkgDeliverTx(t *testing.T) { + success := true + ctx, tx, vmHandler := setupAddPkg(success) + + var simulate bool + + ctx = ctx.WithMode(sdk.RunTxModeDeliver) + simulate = false + tx.Fee.GasWanted = 500000 + gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted) + msgs := tx.GetMsgs() + res := vmHandler.Process(gctx, msgs[0]) + gasDeliver := gctx.GasMeter().GasConsumed() + + assert.True(t, res.IsOK()) + assert.Equal(t, int64(87809), gasDeliver) +} + +// Enough gas for a failed transaction. +func TestAddPkgDeliverTxFailed(t *testing.T) { + success := false + ctx, tx, vmHandler := setupAddPkg(success) + + var simulate bool + + ctx = ctx.WithMode(sdk.RunTxModeDeliver) + simulate = false + tx.Fee.GasWanted = 500000 + gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted) + msgs := tx.GetMsgs() + res := vmHandler.Process(gctx, msgs[0]) + gasDeliver := gctx.GasMeter().GasConsumed() + + assert.False(t, res.IsOK()) + assert.Equal(t, int64(17989), gasDeliver) +} + +// Not enough gas for a failed transaction. +func TestAddPkgDeliverTxFailedNoGas(t *testing.T) { + success := false + ctx, tx, vmHandler := setupAddPkg(success) + + var simulate bool + + ctx = ctx.WithMode(sdk.RunTxModeDeliver) + simulate = false + tx.Fee.GasWanted = 17988 + gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted) + + var res sdk.Result + abort := false + + defer func() { + if r := recover(); r != nil { + switch r.(type) { + case store.OutOfGasException: + res.Error = sdk.ABCIError(std.ErrOutOfGas("")) + abort = true + default: + t.Errorf("should panic on OutOfGasException only") + } + assert.True(t, abort) + assert.False(t, res.IsOK()) + gasCheck := gctx.GasMeter().GasConsumed() + assert.Equal(t, int64(17989), gasCheck) + } else { + t.Errorf("should panic") + } + }() + + msgs := tx.GetMsgs() + res = vmHandler.Process(gctx, msgs[0]) +} + +// Set up a test env for both a successful and a failed tx +func setupAddPkg(success bool) (sdk.Context, sdk.Tx, vmHandler) { + // setup + env := setupTestEnv() + ctx := env.ctx + // conduct base gas meter tests from a non-genesis block since genesis block use infinite gas meter instead. + ctx = ctx.WithBlockHeader(&bft.Header{Height: int64(1)}) + vmHandler := NewHandler(env.vmk) + // Create an account with 10M ugnot (10gnot) + addr := crypto.AddressFromPreimage([]byte("test1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot")) + // success message + var files []*std.MemFile + if success { + files = []*std.MemFile{ + { + Name: "hello.gno", + Body: `package hello + +func Echo() string { + return "hello world" +}`, + }, + } + } else { + // failed message + files = []*std.MemFile{ + { + Name: "hello.gno", + Body: `package hello + +func Echo() UnknowType { + return "hello world" +}`, + }, + } + } + + pkgPath := "gno.land/r/hello" + // create messages and a transaction + msg := NewMsgAddPackage(addr, pkgPath, files) + msgs := []std.Msg{msg} + fee := std.NewFee(500000, std.MustParseCoin("1ugnot")) + tx := std.NewTx(msgs, fee, []std.Signature{}, "") + + return ctx, tx, vmHandler +} diff --git a/gno.land/pkg/sdk/vm/handler.go b/gno.land/pkg/sdk/vm/handler.go index 6c3a97696d6..e1dd31846e7 100644 --- a/gno.land/pkg/sdk/vm/handler.go +++ b/gno.land/pkg/sdk/vm/handler.go @@ -6,7 +6,6 @@ import ( abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/sdk" - "github.com/gnolang/gno/tm2/pkg/sdk/auth" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -37,15 +36,7 @@ func (vh vmHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { // Handle MsgAddPackage. func (vh vmHandler) handleMsgAddPackage(ctx sdk.Context, msg MsgAddPackage) sdk.Result { - amount, err := std.ParseCoins("1000000ugnot") // XXX calculate - if err != nil { - return abciResult(err) - } - err = vh.vm.bank.SendCoins(ctx, msg.Creator, auth.FeeCollectorAddress(), amount) - if err != nil { - return abciResult(err) - } - err = vh.vm.AddPackage(ctx, msg) + err := vh.vm.AddPackage(ctx, msg) if err != nil { return abciResult(err) } @@ -54,16 +45,7 @@ func (vh vmHandler) handleMsgAddPackage(ctx sdk.Context, msg MsgAddPackage) sdk. // Handle MsgCall. func (vh vmHandler) handleMsgCall(ctx sdk.Context, msg MsgCall) (res sdk.Result) { - amount, err := std.ParseCoins("1000000ugnot") // XXX calculate - if err != nil { - return abciResult(err) - } - err = vh.vm.bank.SendCoins(ctx, msg.Caller, auth.FeeCollectorAddress(), amount) - if err != nil { - return abciResult(err) - } - resstr := "" - resstr, err = vh.vm.Call(ctx, msg) + resstr, err := vh.vm.Call(ctx, msg) if err != nil { return abciResult(err) } @@ -81,16 +63,7 @@ func (vh vmHandler) handleMsgCall(ctx sdk.Context, msg MsgCall) (res sdk.Result) // Handle MsgRun. func (vh vmHandler) handleMsgRun(ctx sdk.Context, msg MsgRun) (res sdk.Result) { - amount, err := std.ParseCoins("1000000ugnot") // XXX calculate - if err != nil { - return abciResult(err) - } - err = vh.vm.bank.SendCoins(ctx, msg.Caller, auth.FeeCollectorAddress(), amount) - if err != nil { - return abciResult(err) - } - resstr := "" - resstr, err = vh.vm.Run(ctx, msg) + resstr, err := vh.vm.Run(ctx, msg) if err != nil { return abciResult(err) } diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index e9223143bf5..cbeee69d938 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -96,13 +96,13 @@ func (vm *VMKeeper) Initialize(ms store.MultiStore) { } func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store { - // construct main gnoStore if nil. + // construct main store if nil. if vm.gnoStore == nil { panic("VMKeeper must first be initialized") } switch ctx.Mode() { case sdk.RunTxModeDeliver: - // swap sdk store of existing gnoStore. + // swap sdk store of existing store. // this is needed due to e.g. gas wrappers. baseSDKStore := ctx.Store(vm.baseKey) iavlSDKStore := ctx.Store(vm.iavlKey) @@ -134,12 +134,12 @@ func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store { var reRunPath = regexp.MustCompile(`gno\.land/r/g[a-z0-9]+/run`) // AddPackage adds a package with given fileset. -func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { +func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { creator := msg.Creator pkgPath := msg.Package.Path memPkg := msg.Package deposit := msg.Deposit - store := vm.getGnoStore(ctx) + gnostore := vm.getGnoStore(ctx) // Validate arguments. if creator.IsZero() { @@ -152,7 +152,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { if err := msg.Package.Validate(); err != nil { return ErrInvalidPkgPath(err.Error()) } - if pv := store.GetPackage(pkgPath, false); pv != nil { + if pv := gnostore.GetPackage(pkgPath, false); pv != nil { return ErrInvalidPkgPath("package already exists: " + pkgPath) } @@ -170,7 +170,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { // - check if caller is in Admins or Editors. // - check if namespace is not in pause. - err := vm.bank.SendCoins(ctx, creator, pkgAddr, deposit) + err = vm.bank.SendCoins(ctx, creator, pkgAddr, deposit) if err != nil { return err } @@ -192,27 +192,39 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { gno.MachineOptions{ PkgPath: "", Output: os.Stdout, // XXX - Store: store, - Alloc: store.GetAllocator(), + Store: gnostore, + Alloc: gnostore.GetAllocator(), Context: msgCtx, MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) defer m2.Release() + defer func() { + if r := recover(); r != nil { + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM addpkg panic: %v\n%s\n", + r, m2.String()) + return + } + } + }() m2.RunMemPackage(memPkg, true) - return nil } -// Calls calls a public Gno function (for delivertx). +// Call calls a public Gno function (for delivertx). func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { pkgPath := msg.PkgPath // to import fnc := msg.Func - store := vm.getGnoStore(ctx) + gnostore := vm.getGnoStore(ctx) // Get the package and function type. - pv := store.GetPackage(pkgPath, false) + pv := gnostore.GetPackage(pkgPath, false) pl := gno.PackageNodeLocation(pkgPath) - pn := store.GetBlockNode(pl).(*gno.PackageNode) - ft := pn.GetStaticTypeOf(store, gno.Name(fnc)).(*gno.FuncType) + pn := gnostore.GetBlockNode(pl).(*gno.PackageNode) + ft := pn.GetStaticTypeOf(gnostore, gno.Name(fnc)).(*gno.FuncType) // Make main Package with imports. mpn := gno.NewPackageNode("main", "main", nil) mpn.Define("pkg", gno.TypedValue{T: &gno.PackageType{}, V: pv}) @@ -269,22 +281,27 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { gno.MachineOptions{ PkgPath: "", Output: os.Stdout, // XXX - Store: store, + Store: gnostore, Context: msgCtx, - Alloc: store.GetAllocator(), + Alloc: gnostore.GetAllocator(), MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) + defer m.Release() m.SetActivePackage(mpv) defer func() { if r := recover(); r != nil { - err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n", - r, m.String()) - return + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n", + r, m.String()) + return + } } - m.Release() }() rtvs := m.Eval(xn) - for i, rtv := range rtvs { res = res + rtv.String() if i < len(rtvs)-1 { @@ -299,7 +316,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { caller := msg.Caller pkgAddr := caller - store := vm.getGnoStore(ctx) + gnostore := vm.getGnoStore(ctx) send := msg.Send memPkg := msg.Package @@ -341,34 +358,54 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { gno.MachineOptions{ PkgPath: "", Output: buf, - Store: store, - Alloc: store.GetAllocator(), + Store: gnostore, + Alloc: gnostore.GetAllocator(), Context: msgCtx, MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) + // XXX MsgRun does not have pkgPath. How do we find it on chain? defer m.Release() + defer func() { + if r := recover(); r != nil { + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM run main addpkg panic: %v\n%s\n", + r, m.String()) + return + } + } + }() + _, pv := m.RunMemPackage(memPkg, false) m2 := gno.NewMachineWithOptions( gno.MachineOptions{ PkgPath: "", Output: buf, - Store: store, - Alloc: store.GetAllocator(), + Store: gnostore, + Alloc: gnostore.GetAllocator(), Context: msgCtx, MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) + defer m2.Release() m2.SetActivePackage(pv) defer func() { if r := recover(); r != nil { - err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n", - r, m2.String()) - return + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM run main call panic: %v\n%s\n", + r, m2.String()) + return + } } - m2.Release() }() m2.RunMain() - res = buf.String() return res, nil } @@ -438,10 +475,10 @@ func (vm *VMKeeper) QueryFuncs(ctx sdk.Context, pkgPath string) (fsigs FunctionS // TODO: then, rename to "Eval". func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res string, err error) { alloc := gno.NewAllocator(maxAllocQuery) - store := vm.getGnoStore(ctx) + gnostore := vm.getGnoStore(ctx) pkgAddr := gno.DerivePkgAddr(pkgPath) // Get Package. - pv := store.GetPackage(pkgPath, false) + pv := gnostore.GetPackage(pkgPath, false) if pv == nil { err = ErrInvalidPkgPath(fmt.Sprintf( "package not found: %s", pkgPath)) @@ -468,18 +505,24 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res gno.MachineOptions{ PkgPath: pkgPath, Output: os.Stdout, // XXX - Store: store, + Store: gnostore, Context: msgCtx, Alloc: alloc, MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) + defer m.Release() defer func() { if r := recover(); r != nil { - err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval panic: %v\n%s\n", - r, m.String()) - return + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval panic: %v\n%s\n", + r, m.String()) + return + } } - m.Release() }() rtvs := m.Eval(xx) res = "" @@ -498,10 +541,10 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res // TODO: then, rename to "EvalString". func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string) (res string, err error) { alloc := gno.NewAllocator(maxAllocQuery) - store := vm.getGnoStore(ctx) + gnostore := vm.getGnoStore(ctx) pkgAddr := gno.DerivePkgAddr(pkgPath) // Get Package. - pv := store.GetPackage(pkgPath, false) + pv := gnostore.GetPackage(pkgPath, false) if pv == nil { err = ErrInvalidPkgPath(fmt.Sprintf( "package not found: %s", pkgPath)) @@ -528,18 +571,24 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string gno.MachineOptions{ PkgPath: pkgPath, Output: os.Stdout, // XXX - Store: store, + Store: gnostore, Context: msgCtx, Alloc: alloc, MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), }) + defer m.Release() defer func() { if r := recover(); r != nil { - err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval string panic: %v\n%s\n", - r, m.String()) - return + switch r.(type) { + case store.OutOfGasException: // panic in consumeGas() + panic(r) + default: + err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval string panic: %v\n%s\n", + r, m.String()) + return + } } - m.Release() }() rtvs := m.Eval(xx) if len(rtvs) != 1 { diff --git a/gnovm/pkg/gnolang/alloc.go b/gnovm/pkg/gnolang/alloc.go index a83f8102a2b..495be0d2dc2 100644 --- a/gnovm/pkg/gnolang/alloc.go +++ b/gnovm/pkg/gnolang/alloc.go @@ -100,6 +100,7 @@ func (alloc *Allocator) Allocate(size int64) { // this can happen for map items just prior to assignment. return } + alloc.bytes += size if alloc.bytes > alloc.maxBytes { panic("allocation limit exceeded") diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index ea7be1d1f22..018fac66e64 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -14,6 +14,8 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/store" + "github.com/gnolang/overflow" ) // Exception represents a panic that originates from a gno program. @@ -53,11 +55,10 @@ type Machine struct { CheckTypes bool // not yet used ReadOnly bool MaxCycles int64 - - Output io.Writer - Store Store - Context interface{} - + Output io.Writer + Store Store + Context interface{} + GasMeter store.GasMeter // PanicScope is incremented each time a panic occurs and is reset to // zero when it is recovered. PanicScope uint @@ -96,6 +97,7 @@ type MachineOptions struct { Alloc *Allocator // or see MaxAllocBytes. MaxAllocBytes int64 // or 0 for no limit. MaxCycles int64 // or 0 for no limit. + GasMeter store.GasMeter } // the machine constructor gets spammed @@ -120,6 +122,8 @@ func NewMachineWithOptions(opts MachineOptions) *Machine { checkTypes := opts.CheckTypes readOnly := opts.ReadOnly maxCycles := opts.MaxCycles + vmGasMeter := opts.GasMeter + output := opts.Output if output == nil { output = os.Stdout @@ -154,6 +158,7 @@ func NewMachineWithOptions(opts MachineOptions) *Machine { mm.Output = output mm.Store = store mm.Context = context + mm.GasMeter = vmGasMeter if pv != nil { mm.SetActivePackage(pv) @@ -967,10 +972,17 @@ const ( OpReturnCallDefers Op = 0xD7 // TODO rename? ) +const GasFactorCPU int64 = 1 + //---------------------------------------- // "CPU" steps. func (m *Machine) incrCPU(cycles int64) { + if m.GasMeter != nil { + gasCPU := overflow.Mul64p(cycles, GasFactorCPU) + m.GasMeter.ConsumeGas(gasCPU, "CPUCycles") + } + m.Cycles += cycles if m.MaxCycles != 0 && m.Cycles > m.MaxCycles { panic("CPU cycle overrun") diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go index 6abc4380e89..a0ab1cb1dd0 100644 --- a/tm2/pkg/sdk/auth/ante.go +++ b/tm2/pkg/sdk/auth/ante.go @@ -393,6 +393,10 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { if fgd == gpd { prod1 := big.NewInt(0).Mul(fga, gpg) // fee amount * price gas prod2 := big.NewInt(0).Mul(fgw, gpa) // fee gas * price amount + // This is equivalent to checking + // That the Fee / GasWanted ratio is greater than or equal to the minimum GasPrice per gas. + // This approach helps us avoid dealing with configurations where the value of + // the minimum gas price is set to 0.00001ugnot/gas. if prod1.Cmp(prod2) >= 0 { return sdk.Result{} } else { diff --git a/tm2/pkg/store/gas/store.go b/tm2/pkg/store/gas/store.go index bcd0cb7ee80..4ffe46dc275 100644 --- a/tm2/pkg/store/gas/store.go +++ b/tm2/pkg/store/gas/store.go @@ -2,6 +2,7 @@ package gas import ( "github.com/gnolang/gno/tm2/pkg/store/types" + "github.com/gnolang/overflow" ) var _ types.Store = &Store{} @@ -29,8 +30,8 @@ func (gs *Store) Get(key []byte) (value []byte) { gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, types.GasReadCostFlatDesc) value = gs.parent.Get(key) - // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasReadPerByteDesc) + gas := overflow.Mul64p(gs.gasConfig.ReadCostPerByte, types.Gas(len(value))) + gs.gasMeter.ConsumeGas(gas, types.GasReadPerByteDesc) return value } @@ -39,8 +40,9 @@ func (gs *Store) Get(key []byte) (value []byte) { func (gs *Store) Set(key []byte, value []byte) { types.AssertValidValue(value) gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, types.GasWriteCostFlatDesc) - // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*types.Gas(len(value)), types.GasWritePerByteDesc) + + gas := overflow.Mul64p(gs.gasConfig.WriteCostPerByte, types.Gas(len(value))) + gs.gasMeter.ConsumeGas(gas, types.GasWritePerByteDesc) gs.parent.Set(key, value) } @@ -156,7 +158,7 @@ func (gi *gasIterator) Close() { // based on the current value's length. func (gi *gasIterator) consumeSeekGas() { value := gi.Value() - - gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasValuePerByteDesc) + gas := overflow.Mul64p(gi.gasConfig.ReadCostPerByte, types.Gas(len(value))) gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, types.GasIterNextCostFlatDesc) + gi.gasMeter.ConsumeGas(gas, types.GasValuePerByteDesc) }