From 2908965a8df8514351888d5087832cc0ce53cb09 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sat, 19 Nov 2022 23:08:06 +0300 Subject: [PATCH] cli/vm: add --gas parameter to load* commands It's useful to reproduce different execution problems including executions stopped because of GAS limit. Satoshi representation is deliberately used, because that's what is usually found in logs. --- cli/vm/cli.go | 61 ++++++++++++++++++++++++++++++---------------- cli/vm/cli_test.go | 8 ++++++ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/cli/vm/cli.go b/cli/vm/cli.go index 27f01a7f6..b0e8be324 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -60,15 +60,22 @@ const ( const ( verboseFlagFullName = "verbose" historicFlagFullName = "historic" + gasFlagFullName = "gas" backwardsFlagFullName = "backwards" diffFlagFullName = "diff" ) -var historicFlag = cli.IntFlag{ - Name: historicFlagFullName, - Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " + - "Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.", -} +var ( + historicFlag = cli.IntFlag{ + Name: historicFlagFullName, + Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " + + "Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.", + } + gasFlag = cli.Int64Flag{ + Name: gasFlagFullName, + Usage: "GAS limit for this execution (integer number, satoshi).", + } +) var commands = []cli.Command{ { @@ -143,8 +150,8 @@ Example: { Name: "loadnef", Usage: "Load a NEF-consistent script into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadnef [--historic ] [, ...]`, - Flags: []cli.Flag{historicFlag}, + UsageText: `loadnef [--historic ] [--gas ] [, ...]`, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` and parameters are mandatory. ` + cmdargs.SignersParsingDoc + ` @@ -156,8 +163,8 @@ Example: { Name: "loadbase64", Usage: "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadbase64 [--historic ] [, ...]`, - Flags: []cli.Flag{historicFlag}, + UsageText: `loadbase64 [--historic ] [--gas ] [, ...]`, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` is mandatory parameter. ` + cmdargs.SignersParsingDoc + ` @@ -169,8 +176,8 @@ Example: { Name: "loadhex", Usage: "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadhex [--historic ] [, ...]`, - Flags: []cli.Flag{historicFlag}, + UsageText: `loadhex [--historic ] [--gas ] [, ...]`, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` is mandatory parameter. ` + cmdargs.SignersParsingDoc + ` @@ -182,8 +189,8 @@ Example: { Name: "loadgo", Usage: "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes", - UsageText: `loadgo [--historic ] [, ...]`, - Flags: []cli.Flag{historicFlag}, + UsageText: `loadgo [--historic ] [--gas ] [, ...]`, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: ` is mandatory parameter. ` + cmdargs.SignersParsingDoc + ` @@ -195,8 +202,8 @@ Example: { Name: "loadtx", Usage: "Load transaction into the VM from chain or from parameter context file", - UsageText: `loadtx [--historic ] `, - Flags: []cli.Flag{historicFlag}, + UsageText: `loadtx [--historic ] [--gas ] `, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: `Load transaction into the VM from chain or from parameter context file. The transaction script will be loaded into VM; the resulting execution context will use the provided transaction as script container including its signers, hash and nonce. @@ -209,8 +216,8 @@ Example: { Name: "loaddeployed", Usage: "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes", - UsageText: `loaddeployed [--historic ] [, ...]`, - Flags: []cli.Flag{historicFlag}, + UsageText: `loaddeployed [--historic ] [--gas ] [, ...]`, + Flags: []cli.Flag{historicFlag, gasFlag}, Description: `Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes. If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded. @@ -640,12 +647,22 @@ func handleSlots(c *cli.Context) error { // prepareVM retrieves --historic flag from context (if set) and resets app state // (to the specified historic height if given). func prepareVM(c *cli.Context, tx *transaction.Transaction) error { + var err error if c.IsSet(historicFlagFullName) { height := c.Int(historicFlagFullName) - return resetState(c.App, tx, uint32(height)) + err = resetState(c.App, tx, uint32(height)) + } else { + err = resetState(c.App, tx) } - - return resetState(c.App, tx) + if err != nil { + return err + } + if c.IsSet(gasFlagFullName) { + gas := c.Int64(gasFlagFullName) + v := getVMFromContext(c.App) + v.GasLimit = gas + } + return nil } func handleLoadNEF(c *cli.Context) error { @@ -856,7 +873,9 @@ func handleLoadDeployed(c *cli.Context) error { return err } ic = getInteropContextFromContext(c.App) // fetch newly-created IC. - ic.ReuseVM(ic.VM) // clear previously loaded program and context. + gasLimit := ic.VM.GasLimit + ic.ReuseVM(ic.VM) // clear previously loaded program and context. + ic.VM.GasLimit = gasLimit ic.VM.LoadScriptWithHash(cs.NEF.Script, cs.Hash, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", ic.VM.Context().LenInstr()) setManifestInContext(c.App, &cs.Manifest) diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 2ee983055..0fdc20964 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -1118,6 +1118,8 @@ func TestLoadtx(t *testing.T) { e.runProg(t, "loadtx "+tx.Hash().StringLE(), // hash LE "run", + "loadtx --gas 10000 "+tx.Hash().StringLE(), // with GAS + "run", "loadtx 0x"+tx.Hash().StringLE(), // hash LE with 0x prefix "run", "loadtx '"+tmp+"'", // Tx from parameter context file. @@ -1128,6 +1130,8 @@ func TestLoadtx(t *testing.T) { e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, 1) e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkError(t, errors.New("at instruction 3 (PACK): gas limit is exceeded")) + e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, 1) e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, 1) @@ -1147,6 +1151,8 @@ func TestLoaddeployed(t *testing.T) { e.runProg(t, "loaddeployed "+h.StringLE(), // hash LE "run get 1", + "loaddeployed --gas 420000 "+h.StringLE(), // gas-limited + "run get 1", "loaddeployed 0x"+h.StringLE(), // hash LE with 0x prefix "run get 1", "loaddeployed 1", // contract ID @@ -1172,6 +1178,8 @@ func TestLoaddeployed(t *testing.T) { e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, []byte{2}) e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkError(t, errors.New("at instruction 63 (SYSCALL): failed to invoke syscall 837311890: insufficient amount of gas")) + e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, []byte{2}) e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, []byte{2})