Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consolidate vm gas consumption #1430

Merged
merged 16 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions gno.land/cmd/gnoland/testdata/addpkg.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,34 @@
## 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: 117354
-- stdout.call.success --
("hello world!" string)
OK!
GAS WANTED: 2000000
GAS USED: 52909
2 changes: 1 addition & 1 deletion gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,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")

Expand Down Expand Up @@ -131,7 +132,6 @@ func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger log.Logger, m
}

cfg.Logger = logger

return NewAppWithOptions(cfg)
}

Expand Down
2 changes: 1 addition & 1 deletion gno.land/pkg/sdk/vm/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func setupTestEnv() testEnv {
ms.MountStoreWithDB(iavlCapKey, iavl.StoreConstructor, db)
ms.LoadLatestVersion()

ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNopLogger())
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.TestingLogger())
acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount)
bank := bankm.NewBankKeeper(acck)
stdlibsDir := filepath.Join("..", "..", "..", "..", "gnovm", "stdlibs")
Expand Down
177 changes: 177 additions & 0 deletions gno.land/pkg/sdk/vm/gas_test.go
Original file line number Diff line number Diff line change
@@ -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/jaekwon/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, gasCheck, int64(3231))
} 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, gasDeliver, int64(87809))
}

// 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, gasDeliver, int64(17989))
}

// 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, gasCheck, int64(17989))
} 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
}
33 changes: 3 additions & 30 deletions gno.land/pkg/sdk/vm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
piux2 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return abciResult(err)
}
err = vh.vm.AddPackage(ctx, msg)
err := vh.vm.AddPackage(ctx, msg)
if err != nil {
return abciResult(err)
}
Expand All @@ -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)
piux2 marked this conversation as resolved.
Show resolved Hide resolved
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)
}
Expand All @@ -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)
piux2 marked this conversation as resolved.
Show resolved Hide resolved
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)
}
Expand Down
Loading