github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/cli/vm/cli.go (about)

     1  package vm
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/elliptic"
     6  	"encoding/base64"
     7  	"encoding/binary"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"math/big"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"text/tabwriter"
    18  
    19  	"github.com/chzyer/readline"
    20  	"github.com/kballard/go-shellquote"
    21  	"github.com/nspcc-dev/neo-go/cli/cmdargs"
    22  	"github.com/nspcc-dev/neo-go/cli/flags"
    23  	"github.com/nspcc-dev/neo-go/cli/options"
    24  	"github.com/nspcc-dev/neo-go/cli/paramcontext"
    25  	"github.com/nspcc-dev/neo-go/pkg/compiler"
    26  	"github.com/nspcc-dev/neo-go/pkg/config"
    27  	"github.com/nspcc-dev/neo-go/pkg/core"
    28  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    29  	"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
    30  	"github.com/nspcc-dev/neo-go/pkg/core/native"
    31  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    32  	"github.com/nspcc-dev/neo-go/pkg/core/storage"
    33  	"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
    34  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    35  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    36  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    37  	"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
    38  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    39  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    40  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    41  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
    42  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    43  	"github.com/nspcc-dev/neo-go/pkg/util"
    44  	"github.com/nspcc-dev/neo-go/pkg/util/slice"
    45  	"github.com/nspcc-dev/neo-go/pkg/vm"
    46  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    47  	"github.com/urfave/cli"
    48  	"go.uber.org/zap"
    49  	"go.uber.org/zap/zapcore"
    50  )
    51  
    52  const (
    53  	chainKey            = "chain"
    54  	chainCfgKey         = "chainCfg"
    55  	icKey               = "ic"
    56  	contractStateKey    = "contractState"
    57  	exitFuncKey         = "exitFunc"
    58  	readlineInstanceKey = "readlineKey"
    59  	printLogoKey        = "printLogoKey"
    60  )
    61  
    62  // Various flag names.
    63  const (
    64  	verboseFlagFullName   = "verbose"
    65  	historicFlagFullName  = "historic"
    66  	gasFlagFullName       = "gas"
    67  	backwardsFlagFullName = "backwards"
    68  	diffFlagFullName      = "diff"
    69  	hashFlagFullName      = "hash"
    70  )
    71  
    72  var (
    73  	historicFlag = cli.IntFlag{
    74  		Name: historicFlagFullName,
    75  		Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
    76  			"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.",
    77  	}
    78  	gasFlag = cli.Int64Flag{
    79  		Name:  gasFlagFullName,
    80  		Usage: "GAS limit for this execution (integer number, satoshi).",
    81  	}
    82  	hashFlag = cli.StringFlag{
    83  		Name:  hashFlagFullName,
    84  		Usage: "Smart-contract hash in LE form or address",
    85  	}
    86  )
    87  
    88  var commands = []cli.Command{
    89  	{
    90  		Name:        "exit",
    91  		Usage:       "Exit the VM prompt",
    92  		UsageText:   "exit",
    93  		Description: "Exit the VM prompt.",
    94  		Action:      handleExit,
    95  	},
    96  	{
    97  		Name:        "ip",
    98  		Usage:       "Show current instruction",
    99  		UsageText:   "ip",
   100  		Description: "Show current instruction.",
   101  		Action:      handleIP,
   102  	},
   103  	{
   104  		Name:      "break",
   105  		Usage:     "Place a breakpoint",
   106  		UsageText: `break <ip>`,
   107  		Description: `<ip> is mandatory parameter.
   108  
   109  Example:
   110  > break 12`,
   111  		Action: handleBreak,
   112  	},
   113  	{
   114  		Name:      "jump",
   115  		Usage:     "Jump to the specified instruction (absolute IP value)",
   116  		UsageText: `jump <ip>`,
   117  		Description: `<ip> is mandatory parameter (absolute IP value).
   118  
   119  Example:
   120  > jump 12`,
   121  		Action: handleJump,
   122  	},
   123  	{
   124  		Name:        "estack",
   125  		Usage:       "Show evaluation stack contents",
   126  		UsageText:   "estack",
   127  		Description: "Show evaluation stack contents.",
   128  		Action:      handleXStack,
   129  	},
   130  	{
   131  		Name:        "istack",
   132  		Usage:       "Show invocation stack contents",
   133  		UsageText:   "istack",
   134  		Description: "Show invocation stack contents.",
   135  		Action:      handleXStack,
   136  	},
   137  	{
   138  		Name:        "sslot",
   139  		Usage:       "Show static slot contents",
   140  		UsageText:   "sslot",
   141  		Description: "Show static slot contents.",
   142  		Action:      handleSlots,
   143  	},
   144  	{
   145  		Name:        "lslot",
   146  		Usage:       "Show local slot contents",
   147  		UsageText:   "lslot",
   148  		Description: "Show local slot contents",
   149  		Action:      handleSlots,
   150  	},
   151  	{
   152  		Name:        "aslot",
   153  		Usage:       "Show arguments slot contents",
   154  		UsageText:   "aslot",
   155  		Description: "Show arguments slot contents.",
   156  		Action:      handleSlots,
   157  	},
   158  	{
   159  		Name:      "loadnef",
   160  		Usage:     "Load a NEF (possibly with a contract hash) into the VM optionally using provided scoped signers in the context",
   161  		UsageText: `loadnef [--historic <height>] [--gas <int>] [--hash <hash-or-address>] <file> [<manifest>] [-- <signer-with-scope>, ...]`,
   162  		Flags:     []cli.Flag{historicFlag, gasFlag, hashFlag},
   163  		Description: `<file> parameter is mandatory, <manifest> parameter (if omitted) will
   164     be guessed from the <file> parameter by replacing '.nef' suffix with '.manifest.json'
   165     suffix.
   166  
   167  ` + cmdargs.SignersParsingDoc + `
   168  
   169  Example:
   170  > loadnef /path/to/script.nef /path/to/manifest.json`,
   171  		Action: handleLoadNEF,
   172  	},
   173  	{
   174  		Name:      "loadbase64",
   175  		Usage:     "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes",
   176  		UsageText: `loadbase64 [--historic <height>] [--gas <int>] <string> [-- <signer-with-scope>, ...]`,
   177  		Flags:     []cli.Flag{historicFlag, gasFlag},
   178  		Description: `<string> is mandatory parameter.
   179  
   180  ` + cmdargs.SignersParsingDoc + `
   181  
   182  Example:
   183  > loadbase64 AwAQpdToAAAADBQV9ehtQR1OrVZVhtHtoUHRfoE+agwUzmFvf3Rhfg/EuAVYOvJgKiON9j8TwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4`,
   184  		Action: handleLoadBase64,
   185  	},
   186  	{
   187  		Name:      "loadhex",
   188  		Usage:     "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes",
   189  		UsageText: `loadhex [--historic <height>] [--gas <int>] <string> [-- <signer-with-scope>, ...]`,
   190  		Flags:     []cli.Flag{historicFlag, gasFlag},
   191  		Description: `<string> is mandatory parameter.
   192  
   193  ` + cmdargs.SignersParsingDoc + `
   194  
   195  Example:
   196  > loadhex 0c0c48656c6c6f20776f726c6421`,
   197  		Action: handleLoadHex,
   198  	},
   199  	{
   200  		Name:      "loadgo",
   201  		Usage:     "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes and setting provided hash",
   202  		UsageText: `loadgo [--historic <height>] [--gas <int>] [--hash <hash-or-address>] <file> [-- <signer-with-scope>, ...]`,
   203  		Flags:     []cli.Flag{historicFlag, gasFlag, hashFlag},
   204  		Description: `<file> is mandatory parameter.
   205  
   206  ` + cmdargs.SignersParsingDoc + `
   207  
   208  Example:
   209  > loadgo /path/to/file.go`,
   210  		Action: handleLoadGo,
   211  	},
   212  	{
   213  		Name:      "loadtx",
   214  		Usage:     "Load transaction into the VM from chain or from parameter context file",
   215  		UsageText: `loadtx [--historic <height>] [--gas <int>] <file-or-hash>`,
   216  		Flags:     []cli.Flag{historicFlag, gasFlag},
   217  		Description: `Load transaction into the VM from chain or from parameter context file.
   218     The transaction script will be loaded into VM; the resulting execution context
   219     will use the provided transaction as script container including its signers,
   220     hash and nonce. It'll also use transaction's system fee value as GAS limit if
   221     --gas option is not used.
   222  
   223  <file-or-hash> is mandatory parameter.
   224  
   225  Example:
   226  > loadtx /path/to/file`,
   227  		Action: handleLoadTx,
   228  	},
   229  	{
   230  		Name:      "loaddeployed",
   231  		Usage:     "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes",
   232  		UsageText: `loaddeployed [--historic <height>] [--gas <int>] <hash-or-address-or-id>  [-- <signer-with-scope>, ...]`,
   233  		Flags:     []cli.Flag{historicFlag, gasFlag},
   234  		Description: `Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes.
   235  If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded.
   236  
   237  <hash-or-address-or-id> is mandatory parameter.
   238  
   239  ` + cmdargs.SignersParsingDoc + `
   240  
   241  Example:
   242  > loaddeployed 0x0000000009070e030d0f0e020d0c06050e030c02`,
   243  		Action: handleLoadDeployed,
   244  	},
   245  	{
   246  		Name:        "reset",
   247  		Usage:       "Unload compiled script from the VM and reset context to proper (possibly, historic) state",
   248  		UsageText:   "reset",
   249  		Flags:       []cli.Flag{historicFlag},
   250  		Description: "Unload compiled script from the VM and reset context to proper (possibly, historic) state.",
   251  		Action:      handleReset,
   252  	},
   253  	{
   254  		Name:      "parse",
   255  		Usage:     "Parse provided argument and convert it into other possible formats",
   256  		UsageText: `parse <arg>`,
   257  		Description: `<arg> is an argument which is tried to be interpreted as an item of different types
   258  and converted to other formats. Strings are escaped and output in quotes.`,
   259  		Action: handleParse,
   260  	},
   261  	{
   262  		Name:      "run",
   263  		Usage:     "Usage Execute the current loaded script",
   264  		UsageText: `run [<method> [<parameter>...]]`,
   265  		Description: `<method> is a contract method, specified in manifest. It can be '_' which will push
   266          parameters onto the stack and execute from the current offset.
   267  <parameter> is a parameter (can be repeated multiple times) that can be specified
   268          using the same rules as for 'contract testinvokefunction' command:
   269  
   270  ` + cmdargs.ParamsParsingDoc + `
   271  
   272  Example:
   273  > run put int:5 string:some_string_value`,
   274  		Action: handleRun,
   275  	},
   276  	{
   277  		Name:        "cont",
   278  		Usage:       "Continue execution of the current loaded script",
   279  		UsageText:   "cont",
   280  		Description: "Continue execution of the current loaded script.",
   281  		Action:      handleCont,
   282  	},
   283  	{
   284  		Name:      "step",
   285  		Usage:     "Step (n) instruction in the program",
   286  		UsageText: `step [<n>]`,
   287  		Description: `<n> is optional parameter to specify number of instructions to run.
   288  
   289  Example:
   290  > step 10`,
   291  		Action: handleStep,
   292  	},
   293  	{
   294  		Name:      "stepinto",
   295  		Usage:     "Stepinto instruction to take in the debugger",
   296  		UsageText: "stepinto",
   297  		Description: `Stepinto instruction to take in the debugger.
   298  
   299  Example:
   300  > stepinto`,
   301  		Action: handleStepInto,
   302  	},
   303  	{
   304  		Name:      "stepout",
   305  		Usage:     "Stepout instruction to take in the debugger",
   306  		UsageText: "stepout",
   307  		Description: `Stepout instruction to take in the debugger.
   308  
   309  Example:
   310  > stepout`,
   311  		Action: handleStepOut,
   312  	},
   313  	{
   314  		Name:      "stepover",
   315  		Usage:     "Stepover instruction to take in the debugger",
   316  		UsageText: "stepover",
   317  		Description: `Stepover instruction to take in the debugger.
   318  
   319  Example:
   320  > stepover`,
   321  		Action: handleStepOver,
   322  	},
   323  	{
   324  		Name:        "ops",
   325  		Usage:       "Dump opcodes of the current loaded program",
   326  		UsageText:   "ops",
   327  		Description: "Dump opcodes of the current loaded program",
   328  		Action:      handleOps,
   329  	},
   330  	{
   331  		Name:        "events",
   332  		Usage:       "Dump events emitted by the current loaded program",
   333  		UsageText:   "events",
   334  		Description: "Dump events emitted by the current loaded program",
   335  		Action:      handleEvents,
   336  	},
   337  	{
   338  		Name:      "env",
   339  		Usage:     "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)",
   340  		UsageText: `env [-v]`,
   341  		Flags: []cli.Flag{
   342  			cli.BoolFlag{
   343  				Name:  verboseFlagFullName + ",v",
   344  				Usage: "Print the whole blockchain node configuration.",
   345  			},
   346  		},
   347  		Description: `Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration).
   348  
   349  Example:
   350  > env -v`,
   351  		Action: handleEnv,
   352  	},
   353  	{
   354  		Name:      "storage",
   355  		Usage:     "Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation",
   356  		UsageText: `storage <hash-or-address-or-id> [<prefix>] [--backwards] [--diff]`,
   357  		Flags: []cli.Flag{
   358  			cli.BoolFlag{
   359  				Name:  backwardsFlagFullName + ",b",
   360  				Usage: "Backwards traversal direction",
   361  			},
   362  			cli.BoolFlag{
   363  				Name:  diffFlagFullName + ",d",
   364  				Usage: "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that.",
   365  			},
   366  		},
   367  		Description: `Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation.
   368  Can be used if no script is loaded.
   369  Hex-encoded storage items prefix may be specified (empty by default to return the whole set of storage items).
   370  If seek prefix is not empty, then it's trimmed from the resulting keys.
   371  Items are sorted. Backwards seek direction may be specified (false by default, which means forwards storage seek direction).
   372  It is possible to dump only those storage items that were added or changed during current script invocation (use --diff flag for it).
   373  To dump the whole set of storage changes including removed items use 'changes' command.
   374  
   375  Example:
   376  > storage 0x0000000009070e030d0f0e020d0c06050e030c02 030e --backwards --diff`,
   377  		Action: handleStorage,
   378  	},
   379  	{
   380  		Name:      "changes",
   381  		Usage:     "Dump storage changes as is at the current stage of loaded script invocation",
   382  		UsageText: `changes [<hash-or-address-or-id> [<prefix>]]`,
   383  		Description: `Dump storage changes as is at the current stage of loaded script invocation.
   384  If no script is loaded or executed, then no changes are present.
   385  The contract hash, address or ID may be specified as the first parameter to dump the specified contract storage changes.
   386  Hex-encoded search prefix (without contract ID) may be specified to dump matching storage changes.
   387  Resulting values are not sorted.
   388  
   389  Example:
   390  > changes 0x0000000009070e030d0f0e020d0c06050e030c02 030e`,
   391  		Action: handleChanges,
   392  	},
   393  }
   394  
   395  var completer *readline.PrefixCompleter
   396  
   397  func init() {
   398  	var pcItems []readline.PrefixCompleterInterface
   399  	for _, c := range commands {
   400  		if !c.Hidden {
   401  			var flagsItems []readline.PrefixCompleterInterface
   402  			for _, f := range c.Flags {
   403  				names := strings.SplitN(f.GetName(), ", ", 2) // only long name will be offered
   404  				flagsItems = append(flagsItems, readline.PcItem("--"+names[0]))
   405  			}
   406  			pcItems = append(pcItems, readline.PcItem(c.Name, flagsItems...))
   407  		}
   408  	}
   409  	completer = readline.NewPrefixCompleter(pcItems...)
   410  }
   411  
   412  // Various errors.
   413  var (
   414  	ErrMissingParameter = errors.New("missing argument")
   415  	ErrInvalidParameter = errors.New("can't parse argument")
   416  )
   417  
   418  // CLI object for interacting with the VM.
   419  type CLI struct {
   420  	chain *core.Blockchain
   421  	shell *cli.App
   422  }
   423  
   424  // NewWithConfig returns new CLI instance using provided config and (optionally)
   425  // provided node config for state-backed VM.
   426  func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg config.Config) (*CLI, error) {
   427  	if c.AutoComplete == nil {
   428  		// Autocomplete commands/flags on TAB.
   429  		c.AutoComplete = completer
   430  	}
   431  	l, err := readline.NewEx(c)
   432  	if err != nil {
   433  		return nil, fmt.Errorf("failed to create readline instance: %w", err)
   434  	}
   435  	ctl := cli.NewApp()
   436  	ctl.Name = "VM CLI"
   437  
   438  	// Note: need to set empty `ctl.HelpName` and `ctl.UsageText`, otherwise
   439  	// `filepath.Base(os.Args[0])` will be used which is `neo-go`.
   440  	ctl.HelpName = ""
   441  	ctl.UsageText = ""
   442  
   443  	ctl.Writer = l.Stdout()
   444  	ctl.ErrWriter = l.Stderr()
   445  	ctl.Version = config.Version
   446  	ctl.Usage = "Official VM CLI for NeoGo"
   447  
   448  	// Override default error handler in order not to exit on error.
   449  	ctl.ExitErrHandler = func(context *cli.Context, err error) {}
   450  
   451  	ctl.Commands = commands
   452  
   453  	store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
   454  	if err != nil {
   455  		writeErr(ctl.ErrWriter, fmt.Errorf("failed to open DB, clean in-memory storage will be used: %w", err))
   456  		cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
   457  		store = storage.NewMemoryStore()
   458  	}
   459  
   460  	log, _, logCloser, err := options.HandleLoggingParams(false, cfg.ApplicationConfiguration)
   461  	if err != nil {
   462  		return nil, cli.NewExitError(fmt.Errorf("failed to init logger: %w", err), 1)
   463  	}
   464  	filter := zap.WrapCore(func(z zapcore.Core) zapcore.Core {
   465  		return options.NewFilteringCore(z, func(entry zapcore.Entry) bool {
   466  			// Log only Runtime.Notify messages.
   467  			return entry.Level == zapcore.InfoLevel && entry.Message == runtime.SystemRuntimeLogMessage
   468  		})
   469  	})
   470  	fLog := log.WithOptions(filter)
   471  
   472  	exitF := func(i int) {
   473  		_ = store.Close()
   474  		if logCloser != nil {
   475  			_ = logCloser()
   476  		}
   477  		onExit(i)
   478  	}
   479  
   480  	chain, err := core.NewBlockchain(store, cfg.Blockchain(), fLog)
   481  	if err != nil {
   482  		return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
   483  	}
   484  	// Do not run chain, we need only state-related functionality from it.
   485  	ic, err := chain.GetTestVM(trigger.Application, nil, nil)
   486  	if err != nil {
   487  		return nil, cli.NewExitError(fmt.Errorf("failed to create test VM: %w", err), 1)
   488  	}
   489  
   490  	vmcli := CLI{
   491  		chain: chain,
   492  		shell: ctl,
   493  	}
   494  
   495  	vmcli.shell.Metadata = map[string]any{
   496  		chainKey:            chain,
   497  		chainCfgKey:         cfg,
   498  		icKey:               ic,
   499  		contractStateKey:    new(state.ContractBase),
   500  		exitFuncKey:         exitF,
   501  		readlineInstanceKey: l,
   502  		printLogoKey:        printLogotype,
   503  	}
   504  	changePrompt(vmcli.shell)
   505  	return &vmcli, nil
   506  }
   507  
   508  func getExitFuncFromContext(app *cli.App) func(int) {
   509  	return app.Metadata[exitFuncKey].(func(int))
   510  }
   511  
   512  func getReadlineInstanceFromContext(app *cli.App) *readline.Instance {
   513  	return app.Metadata[readlineInstanceKey].(*readline.Instance)
   514  }
   515  
   516  func getVMFromContext(app *cli.App) *vm.VM {
   517  	return getInteropContextFromContext(app).VM
   518  }
   519  
   520  func getChainFromContext(app *cli.App) *core.Blockchain {
   521  	return app.Metadata[chainKey].(*core.Blockchain)
   522  }
   523  
   524  func getChainConfigFromContext(app *cli.App) config.Config {
   525  	return app.Metadata[chainCfgKey].(config.Config)
   526  }
   527  
   528  func getInteropContextFromContext(app *cli.App) *interop.Context {
   529  	return app.Metadata[icKey].(*interop.Context)
   530  }
   531  
   532  func getContractStateFromContext(app *cli.App) *state.ContractBase {
   533  	return app.Metadata[contractStateKey].(*state.ContractBase)
   534  }
   535  
   536  func getPrintLogoFromContext(app *cli.App) bool {
   537  	return app.Metadata[printLogoKey].(bool)
   538  }
   539  
   540  func setInteropContextInContext(app *cli.App, ic *interop.Context) {
   541  	app.Metadata[icKey] = ic
   542  }
   543  
   544  func setContractStateInContext(app *cli.App, cs *state.ContractBase) {
   545  	app.Metadata[contractStateKey] = cs
   546  }
   547  
   548  func checkVMIsReady(app *cli.App) bool {
   549  	v := getVMFromContext(app)
   550  	if v == nil || !v.Ready() {
   551  		writeErr(app.Writer, errors.New("VM is not ready: no program loaded"))
   552  		return false
   553  	}
   554  	return true
   555  }
   556  
   557  func handleExit(c *cli.Context) error {
   558  	finalizeInteropContext(c.App)
   559  	l := getReadlineInstanceFromContext(c.App)
   560  	_ = l.Close()
   561  	exit := getExitFuncFromContext(c.App)
   562  	fmt.Fprintln(c.App.Writer, "Bye!")
   563  	exit(0)
   564  	return nil
   565  }
   566  
   567  func handleIP(c *cli.Context) error {
   568  	if !checkVMIsReady(c.App) {
   569  		return nil
   570  	}
   571  	v := getVMFromContext(c.App)
   572  	ctx := v.Context()
   573  	if ctx.NextIP() < ctx.LenInstr() {
   574  		ip, opcode := v.Context().NextInstr()
   575  		fmt.Fprintf(c.App.Writer, "instruction pointer at %d (%s)\n", ip, opcode)
   576  	} else {
   577  		fmt.Fprintln(c.App.Writer, "execution has finished")
   578  	}
   579  	return nil
   580  }
   581  
   582  func handleBreak(c *cli.Context) error {
   583  	if !checkVMIsReady(c.App) {
   584  		return nil
   585  	}
   586  	n, err := getInstructionParameter(c)
   587  	if err != nil {
   588  		return err
   589  	}
   590  
   591  	v := getVMFromContext(c.App)
   592  	v.AddBreakPoint(n)
   593  	fmt.Fprintf(c.App.Writer, "breakpoint added at instruction %d\n", n)
   594  	return nil
   595  }
   596  
   597  func handleJump(c *cli.Context) error {
   598  	if !checkVMIsReady(c.App) {
   599  		return nil
   600  	}
   601  	n, err := getInstructionParameter(c)
   602  	if err != nil {
   603  		return err
   604  	}
   605  
   606  	v := getVMFromContext(c.App)
   607  	v.Context().Jump(n)
   608  	fmt.Fprintf(c.App.Writer, "jumped to instruction %d\n", n)
   609  	return nil
   610  }
   611  
   612  func getInstructionParameter(c *cli.Context) (int, error) {
   613  	args := c.Args()
   614  	if len(args) != 1 {
   615  		return 0, fmt.Errorf("%w: <ip>", ErrMissingParameter)
   616  	}
   617  	n, err := strconv.Atoi(args[0])
   618  	if err != nil {
   619  		return 0, fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   620  	}
   621  	return n, nil
   622  }
   623  
   624  func handleXStack(c *cli.Context) error {
   625  	v := getVMFromContext(c.App)
   626  	var stackDump string
   627  	switch c.Command.Name {
   628  	case "estack":
   629  		stackDump = v.DumpEStack()
   630  	case "istack":
   631  		stackDump = v.DumpIStack()
   632  	default:
   633  		return errors.New("unknown stack")
   634  	}
   635  	fmt.Fprintln(c.App.Writer, stackDump)
   636  	return nil
   637  }
   638  
   639  func handleSlots(c *cli.Context) error {
   640  	v := getVMFromContext(c.App)
   641  	vmCtx := v.Context()
   642  	if vmCtx == nil {
   643  		return errors.New("no program loaded")
   644  	}
   645  	var rawSlot string
   646  	switch c.Command.Name {
   647  	case "sslot":
   648  		rawSlot = vmCtx.DumpStaticSlot()
   649  	case "lslot":
   650  		rawSlot = vmCtx.DumpLocalSlot()
   651  	case "aslot":
   652  		rawSlot = vmCtx.DumpArgumentsSlot()
   653  	default:
   654  		return errors.New("unknown slot")
   655  	}
   656  	fmt.Fprintln(c.App.Writer, rawSlot)
   657  	return nil
   658  }
   659  
   660  // prepareVM retrieves --historic flag from context (if set) and resets app state
   661  // (to the specified historic height if given).
   662  func prepareVM(c *cli.Context, tx *transaction.Transaction) error {
   663  	var err error
   664  	if c.IsSet(historicFlagFullName) {
   665  		height := c.Int(historicFlagFullName)
   666  		err = resetState(c.App, tx, uint32(height))
   667  	} else {
   668  		err = resetState(c.App, tx)
   669  	}
   670  	if err != nil {
   671  		return err
   672  	}
   673  	if c.IsSet(gasFlagFullName) {
   674  		gas := c.Int64(gasFlagFullName)
   675  		v := getVMFromContext(c.App)
   676  		v.GasLimit = gas
   677  	}
   678  	return nil
   679  }
   680  
   681  func getHashFlag(c *cli.Context) (util.Uint160, error) {
   682  	if !c.IsSet(hashFlagFullName) {
   683  		return util.Uint160{}, nil
   684  	}
   685  	h, err := flags.ParseAddress(c.String(hashFlagFullName))
   686  	if err != nil {
   687  		return util.Uint160{}, fmt.Errorf("failed to parse contract hash: %w", err)
   688  	}
   689  	return h, nil
   690  }
   691  
   692  func handleLoadNEF(c *cli.Context) error {
   693  	args := c.Args()
   694  	if len(args) < 1 {
   695  		return fmt.Errorf("%w: <nef> is required", ErrMissingParameter)
   696  	}
   697  	nefFile := args[0]
   698  	var (
   699  		manifestFile       string
   700  		signersStartOffset int
   701  	)
   702  	if len(args) == 2 {
   703  		manifestFile = args[1]
   704  	} else if len(args) == 3 {
   705  		if args[1] != cmdargs.CosignersSeparator {
   706  			return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1])
   707  		}
   708  		signersStartOffset = 2
   709  	} else if len(args) > 3 {
   710  		if args[1] == cmdargs.CosignersSeparator {
   711  			signersStartOffset = 2
   712  		} else {
   713  			manifestFile = args[1]
   714  			if args[2] != cmdargs.CosignersSeparator {
   715  				return fmt.Errorf("%w: `%s` was expected as the third parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[2])
   716  			}
   717  			signersStartOffset = 3
   718  		}
   719  	}
   720  	if len(manifestFile) == 0 {
   721  		manifestFile = strings.TrimSuffix(nefFile, ".nef") + ".manifest.json"
   722  	}
   723  	b, err := os.ReadFile(nefFile)
   724  	if err != nil {
   725  		return err
   726  	}
   727  	nef, err := nef.FileFromBytes(b)
   728  	if err != nil {
   729  		return fmt.Errorf("failed to decode NEF file: %w", err)
   730  	}
   731  	m, err := getManifestFromFile(manifestFile)
   732  	if err != nil {
   733  		return fmt.Errorf("failed to read manifest: %w", err)
   734  	}
   735  	var signers []transaction.Signer
   736  	if signersStartOffset != 0 && len(args) > signersStartOffset {
   737  		signers, err = cmdargs.ParseSigners(c.Args()[signersStartOffset:])
   738  		if err != nil {
   739  			return fmt.Errorf("%w: failed to parse signers: %w", ErrInvalidParameter, err)
   740  		}
   741  	}
   742  	err = prepareVM(c, createFakeTransaction(nef.Script, signers))
   743  	if err != nil {
   744  		return err
   745  	}
   746  	h, err := getHashFlag(c)
   747  	if err != nil {
   748  		return err
   749  	}
   750  	cs := &state.ContractBase{
   751  		Hash:     h,
   752  		NEF:      nef,
   753  		Manifest: *m,
   754  	}
   755  	setContractStateInContext(c.App, cs)
   756  
   757  	v := getVMFromContext(c.App)
   758  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
   759  	changePrompt(c.App)
   760  	return nil
   761  }
   762  
   763  func handleLoadBase64(c *cli.Context) error {
   764  	args := c.Args()
   765  	if len(args) < 1 {
   766  		return fmt.Errorf("%w: <string>", ErrMissingParameter)
   767  	}
   768  	b, err := base64.StdEncoding.DecodeString(args[0])
   769  	if err != nil {
   770  		return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   771  	}
   772  	var signers []transaction.Signer
   773  	if len(args) > 1 {
   774  		if args[1] != cmdargs.CosignersSeparator {
   775  			return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1])
   776  		}
   777  		if len(args) < 3 {
   778  			return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator)
   779  		}
   780  		signers, err = cmdargs.ParseSigners(args[2:])
   781  		if err != nil {
   782  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   783  		}
   784  	}
   785  	err = prepareVM(c, createFakeTransaction(b, signers))
   786  	if err != nil {
   787  		return err
   788  	}
   789  	v := getVMFromContext(c.App)
   790  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
   791  	changePrompt(c.App)
   792  	return nil
   793  }
   794  
   795  // createFakeTransaction creates fake transaction with prefilled script, VUB and signers.
   796  func createFakeTransaction(script []byte, signers []transaction.Signer) *transaction.Transaction {
   797  	return &transaction.Transaction{
   798  		Script:  script,
   799  		Signers: signers,
   800  	}
   801  }
   802  
   803  func handleLoadHex(c *cli.Context) error {
   804  	args := c.Args()
   805  	if len(args) < 1 {
   806  		return fmt.Errorf("%w: <string>", ErrMissingParameter)
   807  	}
   808  	b, err := hex.DecodeString(args[0])
   809  	if err != nil {
   810  		return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   811  	}
   812  	var signers []transaction.Signer
   813  	if len(args) > 1 {
   814  		if args[1] != cmdargs.CosignersSeparator {
   815  			return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1])
   816  		}
   817  		if len(args) < 3 {
   818  			return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator)
   819  		}
   820  		signers, err = cmdargs.ParseSigners(args[2:])
   821  		if err != nil {
   822  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   823  		}
   824  	}
   825  	err = prepareVM(c, createFakeTransaction(b, signers))
   826  	if err != nil {
   827  		return err
   828  	}
   829  	v := getVMFromContext(c.App)
   830  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
   831  	changePrompt(c.App)
   832  	return nil
   833  }
   834  
   835  func handleLoadGo(c *cli.Context) error {
   836  	args := c.Args()
   837  	if len(args) < 1 {
   838  		return fmt.Errorf("%w: <file>", ErrMissingParameter)
   839  	}
   840  
   841  	name := strings.TrimSuffix(args[0], ".go")
   842  	ne, di, err := compiler.CompileWithOptions(args[0], nil, &compiler.Options{Name: name})
   843  	if err != nil {
   844  		return fmt.Errorf("failed to compile: %w", err)
   845  	}
   846  
   847  	// Don't perform checks, just load.
   848  	m, err := di.ConvertToManifest(&compiler.Options{})
   849  	if err != nil {
   850  		return fmt.Errorf("can't create manifest: %w", err)
   851  	}
   852  	var signers []transaction.Signer
   853  	if len(args) > 1 {
   854  		if args[1] != cmdargs.CosignersSeparator {
   855  			return fmt.Errorf("%w: `%s` was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1])
   856  		}
   857  		if len(args) < 3 {
   858  			return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator)
   859  		}
   860  		signers, err = cmdargs.ParseSigners(args[2:])
   861  		if err != nil {
   862  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   863  		}
   864  	}
   865  
   866  	err = prepareVM(c, createFakeTransaction(ne.Script, signers))
   867  	if err != nil {
   868  		return err
   869  	}
   870  	h, err := getHashFlag(c)
   871  	if err != nil {
   872  		return err
   873  	}
   874  	cs := &state.ContractBase{
   875  		Hash:     h,
   876  		NEF:      *ne,
   877  		Manifest: *m,
   878  	}
   879  	setContractStateInContext(c.App, cs)
   880  
   881  	v := getVMFromContext(c.App)
   882  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
   883  	changePrompt(c.App)
   884  	return nil
   885  }
   886  
   887  func handleLoadTx(c *cli.Context) error {
   888  	args := c.Args()
   889  	if len(args) < 1 {
   890  		return fmt.Errorf("%w: <file-or-hash>", ErrMissingParameter)
   891  	}
   892  
   893  	var (
   894  		tx  *transaction.Transaction
   895  		err error
   896  	)
   897  	h, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
   898  	if err != nil {
   899  		pc, err := paramcontext.Read(args[0])
   900  		if err != nil {
   901  			return fmt.Errorf("invalid tx hash or path to parameter context: %w", err)
   902  		}
   903  		var ok bool
   904  		tx, ok = pc.Verifiable.(*transaction.Transaction)
   905  		if !ok {
   906  			return errors.New("failed to retrieve transaction from parameter context: verifiable item is not a transaction")
   907  		}
   908  	} else {
   909  		bc := getChainFromContext(c.App)
   910  		tx, _, err = bc.GetTransaction(h)
   911  		if err != nil {
   912  			return fmt.Errorf("failed to get transaction from chain: %w", err)
   913  		}
   914  	}
   915  	err = prepareVM(c, tx)
   916  	if err != nil {
   917  		return err
   918  	}
   919  	v := getVMFromContext(c.App)
   920  	if v.GasLimit == -1 {
   921  		v.GasLimit = tx.SystemFee
   922  	}
   923  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
   924  	changePrompt(c.App)
   925  	return nil
   926  }
   927  
   928  func handleLoadDeployed(c *cli.Context) error {
   929  	err := prepareVM(c, nil) // prepare historic IC if needed (for further historic contract state retrieving).
   930  	if err != nil {
   931  		return err
   932  	}
   933  	if !c.Args().Present() {
   934  		return errors.New("contract hash, address or ID is mandatory argument")
   935  	}
   936  	args := c.Args()
   937  	hashOrID := args[0]
   938  	ic := getInteropContextFromContext(c.App)
   939  	h, err := flags.ParseAddress(hashOrID)
   940  	if err != nil {
   941  		i, err := strconv.ParseInt(hashOrID, 10, 32)
   942  		if err != nil {
   943  			return fmt.Errorf("failed to parse contract hash, address or ID: %w", err)
   944  		}
   945  		h, err = native.GetContractScriptHash(ic.DAO, int32(i))
   946  		if err != nil {
   947  			return fmt.Errorf("failed to retrieve contract hash by ID: %w", err)
   948  		}
   949  	}
   950  	cs, err := ic.GetContract(h) // will return historic contract state.
   951  	if err != nil {
   952  		return fmt.Errorf("contract %s not found: %w", h.StringLE(), err)
   953  	}
   954  
   955  	var signers []transaction.Signer
   956  	if len(args) > 1 {
   957  		if args[1] != cmdargs.CosignersSeparator {
   958  			return fmt.Errorf("%w: %s was expected as the second parameter, got %s", ErrInvalidParameter, cmdargs.CosignersSeparator, args[1])
   959  		}
   960  		if len(args) < 3 {
   961  			return fmt.Errorf("%w: signers expected after `%s`, got none", ErrInvalidParameter, cmdargs.CosignersSeparator)
   962  		}
   963  		signers, err = cmdargs.ParseSigners(args[2:])
   964  		if err != nil {
   965  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
   966  		}
   967  	}
   968  	err = prepareVM(c, createFakeTransaction(cs.NEF.Script, signers)) // prepare VM one more time for proper IC initialization.
   969  	if err != nil {
   970  		return err
   971  	}
   972  	ic = getInteropContextFromContext(c.App) // fetch newly-created IC.
   973  	gasLimit := ic.VM.GasLimit
   974  	ic.ReuseVM(ic.VM) // clear previously loaded program and context.
   975  	ic.VM.GasLimit = gasLimit
   976  	ic.VM.LoadScriptWithHash(cs.NEF.Script, cs.Hash, callflag.All)
   977  	fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", ic.VM.Context().LenInstr())
   978  	setContractStateInContext(c.App, &cs.ContractBase)
   979  	changePrompt(c.App)
   980  	return nil
   981  }
   982  
   983  func handleReset(c *cli.Context) error {
   984  	err := prepareVM(c, nil)
   985  	if err != nil {
   986  		return err
   987  	}
   988  	changePrompt(c.App)
   989  	return nil
   990  }
   991  
   992  // finalizeInteropContext calls finalizer for the current interop context.
   993  func finalizeInteropContext(app *cli.App) {
   994  	ic := getInteropContextFromContext(app)
   995  	ic.Finalize()
   996  }
   997  
   998  // resetInteropContext calls finalizer for current interop context and replaces
   999  // it with the newly created one. If transaction is provided, then its script is
  1000  // loaded into bound VM.
  1001  func resetInteropContext(app *cli.App, tx *transaction.Transaction, height ...uint32) error {
  1002  	finalizeInteropContext(app)
  1003  	bc := getChainFromContext(app)
  1004  	var (
  1005  		newIc *interop.Context
  1006  		err   error
  1007  	)
  1008  	if len(height) != 0 {
  1009  		if tx != nil {
  1010  			tx.ValidUntilBlock = height[0] + 1
  1011  		}
  1012  		newIc, err = bc.GetTestHistoricVM(trigger.Application, tx, height[0]+1)
  1013  		if err != nil {
  1014  			return fmt.Errorf("failed to create historic VM for height %d: %w", height[0], err)
  1015  		}
  1016  	} else {
  1017  		if tx != nil {
  1018  			tx.ValidUntilBlock = bc.BlockHeight() + 1
  1019  		}
  1020  		newIc, err = bc.GetTestVM(trigger.Application, tx, nil)
  1021  		if err != nil {
  1022  			return fmt.Errorf("failed to create VM: %w", err)
  1023  		}
  1024  	}
  1025  	if tx != nil {
  1026  		newIc.VM.LoadWithFlags(tx.Script, callflag.All)
  1027  	}
  1028  
  1029  	setInteropContextInContext(app, newIc)
  1030  	return nil
  1031  }
  1032  
  1033  // resetContractState removes loaded contract state from app context.
  1034  func resetContractState(app *cli.App) {
  1035  	setContractStateInContext(app, nil)
  1036  }
  1037  
  1038  // resetState resets state of the app (clear interop context and manifest) so that it's ready
  1039  // to load new program.
  1040  func resetState(app *cli.App, tx *transaction.Transaction, height ...uint32) error {
  1041  	err := resetInteropContext(app, tx, height...)
  1042  	if err != nil {
  1043  		return err
  1044  	}
  1045  	resetContractState(app)
  1046  	return nil
  1047  }
  1048  
  1049  func getManifestFromFile(name string) (*manifest.Manifest, error) {
  1050  	bs, err := os.ReadFile(name)
  1051  	if err != nil {
  1052  		return nil, fmt.Errorf("%w: can't read manifest", ErrInvalidParameter)
  1053  	}
  1054  
  1055  	var m manifest.Manifest
  1056  	if err := json.Unmarshal(bs, &m); err != nil {
  1057  		return nil, fmt.Errorf("%w: can't unmarshal manifest", ErrInvalidParameter)
  1058  	}
  1059  	return &m, nil
  1060  }
  1061  
  1062  func handleRun(c *cli.Context) error {
  1063  	v := getVMFromContext(c.App)
  1064  	cs := getContractStateFromContext(c.App)
  1065  	args := c.Args()
  1066  	if len(args) != 0 {
  1067  		var (
  1068  			params     []stackitem.Item
  1069  			offset     int
  1070  			err        error
  1071  			runCurrent = args[0] != "_"
  1072  			hasRet     bool
  1073  		)
  1074  
  1075  		_, scParams, err := cmdargs.ParseParams(args[1:], true)
  1076  		if err != nil {
  1077  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
  1078  		}
  1079  		params = make([]stackitem.Item, len(scParams))
  1080  		for i := range scParams {
  1081  			params[i], err = scParams[i].ToStackItem()
  1082  			if err != nil {
  1083  				return fmt.Errorf("failed to convert parameter #%d to stackitem: %w", i, err)
  1084  			}
  1085  		}
  1086  		if runCurrent {
  1087  			if cs == nil {
  1088  				return fmt.Errorf("manifest is not loaded; either use 'run' command to run loaded script from the start or use 'loadgo', 'loadnef' or 'loaddeployed' commands to provide manifest")
  1089  			}
  1090  			md := cs.Manifest.ABI.GetMethod(args[0], len(params))
  1091  			if md == nil {
  1092  				return fmt.Errorf("%w: method not found", ErrInvalidParameter)
  1093  			}
  1094  			hasRet = md.ReturnType != smartcontract.VoidType
  1095  			offset = md.Offset
  1096  			var initOff = -1
  1097  			if initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit, 0); initMD != nil {
  1098  				initOff = initMD.Offset
  1099  			}
  1100  
  1101  			// Clear context loaded by 'loadgo', 'loadnef' or 'loaddeployed' to properly handle LoadNEFMethod.
  1102  			// At the same time, preserve previously set gas limit and the set of breakpoints.
  1103  			ic := getInteropContextFromContext(c.App)
  1104  			gasLimit := v.GasLimit
  1105  			breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
  1106  			ic.ReuseVM(v)
  1107  			v.GasLimit = gasLimit
  1108  			v.LoadNEFMethod(&cs.NEF, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
  1109  			for _, bp := range breaks {
  1110  				v.AddBreakPoint(bp)
  1111  			}
  1112  		}
  1113  		for i := len(params) - 1; i >= 0; i-- {
  1114  			v.Estack().PushVal(params[i])
  1115  		}
  1116  	}
  1117  	runVMWithHandling(c)
  1118  	changePrompt(c.App)
  1119  	return nil
  1120  }
  1121  
  1122  // runVMWithHandling runs VM with handling errors and additional state messages.
  1123  func runVMWithHandling(c *cli.Context) {
  1124  	v := getVMFromContext(c.App)
  1125  	err := v.Run()
  1126  	if err != nil {
  1127  		writeErr(c.App.ErrWriter, err)
  1128  	}
  1129  
  1130  	var (
  1131  		message string
  1132  		dumpNtf bool
  1133  	)
  1134  	switch {
  1135  	case v.HasFailed():
  1136  		message = "" // the error will be printed on return
  1137  		dumpNtf = true
  1138  	case v.HasHalted():
  1139  		message = v.DumpEStack()
  1140  		dumpNtf = true
  1141  	case v.AtBreakpoint():
  1142  		ctx := v.Context()
  1143  		if ctx.NextIP() < ctx.LenInstr() {
  1144  			i, op := ctx.NextInstr()
  1145  			message = fmt.Sprintf("at breakpoint %d (%s)", i, op)
  1146  		} else {
  1147  			message = "execution has finished"
  1148  		}
  1149  	}
  1150  	if dumpNtf {
  1151  		var e string
  1152  		e, err = dumpEvents(c.App)
  1153  		if err == nil && len(e) != 0 {
  1154  			if message != "" {
  1155  				message += "\n"
  1156  			}
  1157  			message += "Events:\n" + e
  1158  		}
  1159  	}
  1160  	if message != "" {
  1161  		fmt.Fprintln(c.App.Writer, message)
  1162  	}
  1163  }
  1164  
  1165  func handleCont(c *cli.Context) error {
  1166  	if !checkVMIsReady(c.App) {
  1167  		return nil
  1168  	}
  1169  	runVMWithHandling(c)
  1170  	changePrompt(c.App)
  1171  	return nil
  1172  }
  1173  
  1174  func handleStep(c *cli.Context) error {
  1175  	var (
  1176  		n   = 1
  1177  		err error
  1178  	)
  1179  
  1180  	if !checkVMIsReady(c.App) {
  1181  		return nil
  1182  	}
  1183  	v := getVMFromContext(c.App)
  1184  	args := c.Args()
  1185  	if len(args) > 0 {
  1186  		n, err = strconv.Atoi(args[0])
  1187  		if err != nil {
  1188  			return fmt.Errorf("%w: %w", ErrInvalidParameter, err)
  1189  		}
  1190  	}
  1191  	v.AddBreakPointRel(n)
  1192  	runVMWithHandling(c)
  1193  	changePrompt(c.App)
  1194  	return nil
  1195  }
  1196  
  1197  func handleStepInto(c *cli.Context) error {
  1198  	return handleStepType(c, "into")
  1199  }
  1200  
  1201  func handleStepOut(c *cli.Context) error {
  1202  	return handleStepType(c, "out")
  1203  }
  1204  
  1205  func handleStepOver(c *cli.Context) error {
  1206  	return handleStepType(c, "over")
  1207  }
  1208  
  1209  func handleStepType(c *cli.Context, stepType string) error {
  1210  	if !checkVMIsReady(c.App) {
  1211  		return nil
  1212  	}
  1213  	v := getVMFromContext(c.App)
  1214  	var err error
  1215  	switch stepType {
  1216  	case "into":
  1217  		err = v.StepInto()
  1218  	case "out":
  1219  		err = v.StepOut()
  1220  	case "over":
  1221  		err = v.StepOver()
  1222  	}
  1223  	if err != nil {
  1224  		return err
  1225  	}
  1226  	_ = handleIP(c)
  1227  	changePrompt(c.App)
  1228  	return nil
  1229  }
  1230  
  1231  func handleOps(c *cli.Context) error {
  1232  	if !checkVMIsReady(c.App) {
  1233  		return nil
  1234  	}
  1235  	v := getVMFromContext(c.App)
  1236  	out := bytes.NewBuffer(nil)
  1237  	v.PrintOps(out)
  1238  	fmt.Fprintln(c.App.Writer, out.String())
  1239  	return nil
  1240  }
  1241  
  1242  func changePrompt(app *cli.App) {
  1243  	v := getVMFromContext(app)
  1244  	l := getReadlineInstanceFromContext(app)
  1245  	if v.Ready() && v.Context().NextIP() >= 0 && v.Context().NextIP() < v.Context().LenInstr() {
  1246  		l.SetPrompt(fmt.Sprintf("\033[32mNEO-GO-VM %d >\033[0m ", v.Context().NextIP()))
  1247  	} else {
  1248  		l.SetPrompt("\033[32mNEO-GO-VM >\033[0m ")
  1249  	}
  1250  }
  1251  
  1252  func handleEvents(c *cli.Context) error {
  1253  	e, err := dumpEvents(c.App)
  1254  	if err != nil {
  1255  		writeErr(c.App.ErrWriter, err)
  1256  		return nil
  1257  	}
  1258  	fmt.Fprintln(c.App.Writer, e)
  1259  	return nil
  1260  }
  1261  
  1262  func handleEnv(c *cli.Context) error {
  1263  	bc := getChainFromContext(c.App)
  1264  	cfg := getChainConfigFromContext(c.App)
  1265  	ic := getInteropContextFromContext(c.App)
  1266  	message := fmt.Sprintf("Chain height: %d\nVM height (may differ from chain height in case of historic call): %d\nNetwork magic: %d\nDB type: %s\n",
  1267  		bc.BlockHeight(), ic.BlockHeight(), bc.GetConfig().Magic, cfg.ApplicationConfiguration.DBConfiguration.Type)
  1268  	if c.Bool(verboseFlagFullName) {
  1269  		cfgBytes, err := json.MarshalIndent(cfg, "", "\t")
  1270  		if err != nil {
  1271  			return fmt.Errorf("failed to marshal node configuration: %w", err)
  1272  		}
  1273  		message += "Node config:\n" + string(cfgBytes) + "\n"
  1274  	}
  1275  	fmt.Fprint(c.App.Writer, message)
  1276  	return nil
  1277  }
  1278  
  1279  func handleStorage(c *cli.Context) error {
  1280  	id, prefix, err := getDumpArgs(c)
  1281  	if err != nil {
  1282  		return err
  1283  	}
  1284  	var (
  1285  		backwards bool
  1286  		seekDepth int
  1287  		ic        = getInteropContextFromContext(c.App)
  1288  	)
  1289  	if c.Bool(backwardsFlagFullName) {
  1290  		backwards = true
  1291  	}
  1292  	if c.Bool(diffFlagFullName) {
  1293  		seekDepth = 1 // take only upper DAO layer which stores only added or updated items.
  1294  	}
  1295  	ic.DAO.Seek(id, storage.SeekRange{
  1296  		Prefix:      prefix,
  1297  		Backwards:   backwards,
  1298  		SearchDepth: seekDepth,
  1299  	}, func(k, v []byte) bool {
  1300  		fmt.Fprintf(c.App.Writer, "%s: %v\n", hex.EncodeToString(k), hex.EncodeToString(v))
  1301  		return true
  1302  	})
  1303  	return nil
  1304  }
  1305  
  1306  func handleChanges(c *cli.Context) error {
  1307  	var (
  1308  		expectedID int32
  1309  		prefix     []byte
  1310  		err        error
  1311  		hasAgs     = c.Args().Present()
  1312  	)
  1313  	if hasAgs {
  1314  		expectedID, prefix, err = getDumpArgs(c)
  1315  		if err != nil {
  1316  			return err
  1317  		}
  1318  	}
  1319  	ic := getInteropContextFromContext(c.App)
  1320  	b := ic.DAO.GetBatch()
  1321  	if b == nil {
  1322  		return nil
  1323  	}
  1324  	ops := storage.BatchToOperations(b)
  1325  	var notFirst bool
  1326  	for _, op := range ops {
  1327  		id := int32(binary.LittleEndian.Uint32(op.Key))
  1328  		if hasAgs && (expectedID != id || (len(prefix) != 0 && !bytes.HasPrefix(op.Key[4:], prefix))) {
  1329  			continue
  1330  		}
  1331  		var message string
  1332  		if notFirst {
  1333  			message += "\n"
  1334  		}
  1335  		message += fmt.Sprintf("Contract ID: %d\nState: %s\nKey: %s\n", id, op.State, hex.EncodeToString(op.Key[4:]))
  1336  		if op.Value != nil {
  1337  			message += fmt.Sprintf("Value: %s\n", hex.EncodeToString(op.Value))
  1338  		}
  1339  		fmt.Fprint(c.App.Writer, message)
  1340  		notFirst = true
  1341  	}
  1342  	return nil
  1343  }
  1344  
  1345  // getDumpArgs is a helper function that retrieves contract ID and search prefix (if given).
  1346  func getDumpArgs(c *cli.Context) (int32, []byte, error) {
  1347  	id, err := getContractID(c)
  1348  	if err != nil {
  1349  		return 0, nil, err
  1350  	}
  1351  	var prefix []byte
  1352  	if c.NArg() > 1 {
  1353  		prefix, err = hex.DecodeString(c.Args().Get(1))
  1354  		if err != nil {
  1355  			return 0, nil, fmt.Errorf("failed to decode prefix from hex: %w", err)
  1356  		}
  1357  	}
  1358  	return id, prefix, nil
  1359  }
  1360  
  1361  // getContractID returns contract ID parsed from the first argument which can be ID,
  1362  // hash or address.
  1363  func getContractID(c *cli.Context) (int32, error) {
  1364  	if !c.Args().Present() {
  1365  		return 0, errors.New("contract hash, address or ID is mandatory argument")
  1366  	}
  1367  	hashOrID := c.Args().Get(0)
  1368  	var ic = getInteropContextFromContext(c.App)
  1369  	h, err := flags.ParseAddress(hashOrID)
  1370  	if err != nil {
  1371  		i, err := strconv.ParseInt(hashOrID, 10, 32)
  1372  		if err != nil {
  1373  			return 0, fmt.Errorf("failed to parse contract hash, address or ID: %w", err)
  1374  		}
  1375  		return int32(i), nil
  1376  	}
  1377  	cs, err := ic.GetContract(h)
  1378  	if err != nil {
  1379  		return 0, fmt.Errorf("contract %s not found: %w", h.StringLE(), err)
  1380  	}
  1381  	return cs.ID, nil
  1382  }
  1383  
  1384  func dumpEvents(app *cli.App) (string, error) {
  1385  	ic := getInteropContextFromContext(app)
  1386  	if len(ic.Notifications) == 0 {
  1387  		return "", nil
  1388  	}
  1389  	b, err := json.MarshalIndent(ic.Notifications, "", "\t")
  1390  	if err != nil {
  1391  		return "", fmt.Errorf("failed to marshal notifications: %w", err)
  1392  	}
  1393  	return string(b), nil
  1394  }
  1395  
  1396  // Run waits for user input from Stdin and executes the passed command.
  1397  func (c *CLI) Run() error {
  1398  	if getPrintLogoFromContext(c.shell) {
  1399  		printLogo(c.shell.Writer)
  1400  	}
  1401  	l := getReadlineInstanceFromContext(c.shell)
  1402  	for {
  1403  		line, err := l.Readline()
  1404  		if errors.Is(err, io.EOF) || errors.Is(err, readline.ErrInterrupt) {
  1405  			return nil // OK, stop execution.
  1406  		}
  1407  		if err != nil {
  1408  			return fmt.Errorf("failed to read input: %w", err) // Critical error, stop execution.
  1409  		}
  1410  
  1411  		args, err := shellquote.Split(line)
  1412  		if err != nil {
  1413  			writeErr(c.shell.ErrWriter, fmt.Errorf("failed to parse arguments: %w", err))
  1414  			continue // Not a critical error, continue execution.
  1415  		}
  1416  
  1417  		err = c.shell.Run(append([]string{"vm"}, args...))
  1418  		if err != nil {
  1419  			writeErr(c.shell.ErrWriter, err) // Various command/flags parsing errors and execution errors.
  1420  		}
  1421  	}
  1422  }
  1423  
  1424  func handleParse(c *cli.Context) error {
  1425  	res, err := Parse(c.Args())
  1426  	if err != nil {
  1427  		return err
  1428  	}
  1429  	fmt.Fprintln(c.App.Writer, res)
  1430  	return nil
  1431  }
  1432  
  1433  // Parse converts it's argument to other formats.
  1434  func Parse(args []string) (string, error) {
  1435  	if len(args) < 1 {
  1436  		return "", ErrMissingParameter
  1437  	}
  1438  	arg := args[0]
  1439  	var buf []byte
  1440  	if val, err := strconv.ParseInt(arg, 10, 64); err == nil {
  1441  		bs := bigint.ToBytes(big.NewInt(val))
  1442  		buf = fmt.Appendf(buf, "Integer to Hex\t%s\n", hex.EncodeToString(bs))
  1443  		buf = fmt.Appendf(buf, "Integer to Base64\t%s\n", base64.StdEncoding.EncodeToString(bs))
  1444  	}
  1445  	noX := strings.TrimPrefix(arg, "0x")
  1446  	if rawStr, err := hex.DecodeString(noX); err == nil {
  1447  		if val, err := util.Uint160DecodeBytesBE(rawStr); err == nil {
  1448  			buf = fmt.Appendf(buf, "BE ScriptHash to Address\t%s\n", address.Uint160ToString(val))
  1449  			buf = fmt.Appendf(buf, "LE ScriptHash to Address\t%s\n", address.Uint160ToString(val.Reverse()))
  1450  		}
  1451  		if pub, err := keys.NewPublicKeyFromBytes(rawStr, elliptic.P256()); err == nil {
  1452  			sh := pub.GetScriptHash()
  1453  			buf = fmt.Appendf(buf, "Public key to BE ScriptHash\t%s\n", sh)
  1454  			buf = fmt.Appendf(buf, "Public key to LE ScriptHash\t%s\n", sh.Reverse())
  1455  			buf = fmt.Appendf(buf, "Public key to Address\t%s\n", address.Uint160ToString(sh))
  1456  		}
  1457  		buf = fmt.Appendf(buf, "Hex to String\t%s\n", fmt.Sprintf("%q", string(rawStr)))
  1458  		buf = fmt.Appendf(buf, "Hex to Integer\t%s\n", bigint.FromBytes(rawStr))
  1459  		buf = fmt.Appendf(buf, "Swap Endianness\t%s\n", hex.EncodeToString(slice.CopyReverse(rawStr)))
  1460  	}
  1461  	if addr, err := address.StringToUint160(arg); err == nil {
  1462  		buf = fmt.Appendf(buf, "Address to BE ScriptHash\t%s\n", addr)
  1463  		buf = fmt.Appendf(buf, "Address to LE ScriptHash\t%s\n", addr.Reverse())
  1464  		buf = fmt.Appendf(buf, "Address to Base64 (BE)\t%s\n", base64.StdEncoding.EncodeToString(addr.BytesBE()))
  1465  		buf = fmt.Appendf(buf, "Address to Base64 (LE)\t%s\n", base64.StdEncoding.EncodeToString(addr.BytesLE()))
  1466  	}
  1467  	if rawStr, err := base64.StdEncoding.DecodeString(arg); err == nil {
  1468  		buf = fmt.Appendf(buf, "Base64 to String\t%s\n", fmt.Sprintf("%q", string(rawStr)))
  1469  		buf = fmt.Appendf(buf, "Base64 to BigInteger\t%s\n", bigint.FromBytes(rawStr))
  1470  		if u, err := util.Uint160DecodeBytesBE(rawStr); err == nil {
  1471  			buf = fmt.Appendf(buf, "Base64 to BE ScriptHash\t%s\n", u.StringBE())
  1472  			buf = fmt.Appendf(buf, "Base64 to LE ScriptHash\t%s\n", u.StringLE())
  1473  			buf = fmt.Appendf(buf, "Base64 to Address (BE)\t%s\n", address.Uint160ToString(u))
  1474  			buf = fmt.Appendf(buf, "Base64 to Address (LE)\t%s\n", address.Uint160ToString(u.Reverse()))
  1475  		}
  1476  	}
  1477  
  1478  	buf = fmt.Appendf(buf, "String to Hex\t%s\n", hex.EncodeToString([]byte(arg)))
  1479  	buf = fmt.Appendf(buf, "String to Base64\t%s\n", base64.StdEncoding.EncodeToString([]byte(arg)))
  1480  
  1481  	res := bytes.NewBuffer(nil)
  1482  	w := tabwriter.NewWriter(res, 0, 4, 4, '\t', 0)
  1483  	if _, err := w.Write(buf); err != nil {
  1484  		return "", err
  1485  	}
  1486  	if err := w.Flush(); err != nil {
  1487  		return "", err
  1488  	}
  1489  	return res.String(), nil
  1490  }
  1491  
  1492  const logo = `
  1493      _   ____________        __________      _    ____  ___
  1494     / | / / ____/ __ \      / ____/ __ \    | |  / /  |/  /
  1495    /  |/ / __/ / / / /_____/ / __/ / / /____| | / / /|_/ / 
  1496   / /|  / /___/ /_/ /_____/ /_/ / /_/ /_____/ |/ / /  / /  
  1497  /_/ |_/_____/\____/      \____/\____/      |___/_/  /_/   
  1498  `
  1499  
  1500  func printLogo(w io.Writer) {
  1501  	fmt.Fprint(w, logo)
  1502  	fmt.Fprintln(w)
  1503  	fmt.Fprintln(w)
  1504  	fmt.Fprintln(w)
  1505  }
  1506  
  1507  func writeErr(w io.Writer, err error) {
  1508  	fmt.Fprintf(w, "Error: %s\n", err)
  1509  }