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

     1  package vm
     2  
     3  import (
     4  	"crypto/elliptic"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"math"
    11  	"math/big"
    12  	"os"
    13  	"text/tabwriter"
    14  	"unicode/utf8"
    15  
    16  	"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
    17  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    18  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    19  	"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
    20  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    21  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
    22  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    23  	"github.com/nspcc-dev/neo-go/pkg/util"
    24  	"github.com/nspcc-dev/neo-go/pkg/util/slice"
    25  	"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
    26  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    27  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    28  	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
    29  )
    30  
    31  type errorAtInstruct struct {
    32  	ip  int
    33  	op  opcode.Opcode
    34  	err any
    35  }
    36  
    37  func (e *errorAtInstruct) Error() string {
    38  	return fmt.Sprintf("at instruction %d (%s): %s", e.ip, e.op, e.err)
    39  }
    40  
    41  func newError(ip int, op opcode.Opcode, err any) *errorAtInstruct {
    42  	return &errorAtInstruct{ip: ip, op: op, err: err}
    43  }
    44  
    45  // StateMessage is a vm state message which could be used as an additional info, for example by cli.
    46  type StateMessage string
    47  
    48  const (
    49  	// MaxInvocationStackSize is the maximum size of an invocation stack.
    50  	MaxInvocationStackSize = 1024
    51  
    52  	// MaxTryNestingDepth is the maximum level of TRY nesting allowed,
    53  	// that is you can't have more exception handling contexts than this.
    54  	MaxTryNestingDepth = 16
    55  
    56  	// MaxStackSize is the maximum number of items allowed to be
    57  	// on all stacks at once.
    58  	MaxStackSize = 2 * 1024
    59  
    60  	maxSHLArg = stackitem.MaxBigIntegerSizeBits
    61  )
    62  
    63  // SyscallHandler is a type for syscall handler.
    64  type SyscallHandler = func(*VM, uint32) error
    65  
    66  // VM represents the virtual machine.
    67  type VM struct {
    68  	state vmstate.State
    69  
    70  	// callback to get interop price
    71  	getPrice func(opcode.Opcode, []byte) int64
    72  
    73  	istack []*Context // invocation stack.
    74  	estack *Stack     // execution stack.
    75  
    76  	uncaughtException stackitem.Item // exception being handled
    77  
    78  	refs refCounter
    79  
    80  	gasConsumed int64
    81  	GasLimit    int64
    82  
    83  	// SyscallHandler handles SYSCALL opcode.
    84  	SyscallHandler func(v *VM, id uint32) error
    85  
    86  	// LoadToken handles CALLT opcode.
    87  	LoadToken func(id int32) error
    88  
    89  	trigger trigger.Type
    90  
    91  	// invTree is a top-level invocation tree (if enabled).
    92  	invTree *invocations.Tree
    93  }
    94  
    95  var (
    96  	bigMinusOne = big.NewInt(-1)
    97  	bigZero     = big.NewInt(0)
    98  	bigOne      = big.NewInt(1)
    99  	bigTwo      = big.NewInt(2)
   100  )
   101  
   102  // New returns a new VM object ready to load AVM bytecode scripts.
   103  func New() *VM {
   104  	return NewWithTrigger(trigger.Application)
   105  }
   106  
   107  // NewWithTrigger returns a new VM for executions triggered by t.
   108  func NewWithTrigger(t trigger.Type) *VM {
   109  	vm := &VM{
   110  		state:   vmstate.None,
   111  		trigger: t,
   112  	}
   113  
   114  	vm.istack = make([]*Context, 0, 8) // Most of invocations use one-two contracts, but they're likely to have internal calls.
   115  	vm.estack = newStack("evaluation", &vm.refs)
   116  	return vm
   117  }
   118  
   119  // SetPriceGetter registers the given PriceGetterFunc in v.
   120  // f accepts vm's Context, current instruction and instruction parameter.
   121  func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
   122  	v.getPrice = f
   123  }
   124  
   125  // Reset allows to reuse existing VM for subsequent executions making them somewhat
   126  // more efficient. It reuses invocation and evaluation stacks as well as VM structure
   127  // itself.
   128  func (v *VM) Reset(t trigger.Type) {
   129  	v.state = vmstate.None
   130  	v.getPrice = nil
   131  	v.istack = v.istack[:0]
   132  	v.estack.elems = v.estack.elems[:0]
   133  	v.uncaughtException = nil
   134  	v.refs = 0
   135  	v.gasConsumed = 0
   136  	v.GasLimit = 0
   137  	v.SyscallHandler = nil
   138  	v.LoadToken = nil
   139  	v.trigger = t
   140  	v.invTree = nil
   141  }
   142  
   143  // GasConsumed returns the amount of GAS consumed during execution.
   144  func (v *VM) GasConsumed() int64 {
   145  	return v.gasConsumed
   146  }
   147  
   148  // AddGas consumes the specified amount of gas. It returns true if gas limit wasn't exceeded.
   149  func (v *VM) AddGas(gas int64) bool {
   150  	v.gasConsumed += gas
   151  	return v.GasLimit < 0 || v.gasConsumed <= v.GasLimit
   152  }
   153  
   154  // Estack returns the evaluation stack, so interop hooks can utilize this.
   155  func (v *VM) Estack() *Stack {
   156  	return v.estack
   157  }
   158  
   159  // Istack returns the invocation stack, so interop hooks can utilize this.
   160  func (v *VM) Istack() []*Context {
   161  	return v.istack
   162  }
   163  
   164  // PrintOps prints the opcodes of the current loaded program to stdout.
   165  func (v *VM) PrintOps(out io.Writer) {
   166  	if out == nil {
   167  		out = os.Stdout
   168  	}
   169  	w := tabwriter.NewWriter(out, 0, 0, 4, ' ', 0)
   170  	fmt.Fprintln(w, "INDEX\tOPCODE\tPARAMETER")
   171  	realctx := v.Context()
   172  	ctx := &Context{sc: realctx.sc}
   173  	for {
   174  		cursor := ""
   175  		instr, parameter, err := ctx.Next()
   176  		if ctx.ip == realctx.ip {
   177  			cursor = "\t<<"
   178  		}
   179  		if err != nil {
   180  			fmt.Fprintf(w, "%d\t%s\tERROR: %s%s\n", ctx.ip, instr, err, cursor)
   181  			break
   182  		}
   183  		var desc = ""
   184  		if parameter != nil {
   185  			switch instr {
   186  			case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL,
   187  				opcode.JMPEQ, opcode.JMPNE,
   188  				opcode.JMPGT, opcode.JMPGE, opcode.JMPLE, opcode.JMPLT,
   189  				opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.CALLL,
   190  				opcode.JMPEQL, opcode.JMPNEL,
   191  				opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLEL, opcode.JMPLTL,
   192  				opcode.PUSHA, opcode.ENDTRY, opcode.ENDTRYL:
   193  				desc = getOffsetDesc(ctx, parameter)
   194  			case opcode.TRY, opcode.TRYL:
   195  				catchP, finallyP := getTryParams(instr, parameter)
   196  				desc = fmt.Sprintf("catch %s, finally %s",
   197  					getOffsetDesc(ctx, catchP), getOffsetDesc(ctx, finallyP))
   198  			case opcode.INITSSLOT:
   199  				desc = fmt.Sprint(parameter[0])
   200  			case opcode.CONVERT, opcode.ISTYPE:
   201  				typ := stackitem.Type(parameter[0])
   202  				desc = fmt.Sprintf("%s (%x)", typ, parameter[0])
   203  			case opcode.INITSLOT:
   204  				desc = fmt.Sprintf("%d local, %d arg", parameter[0], parameter[1])
   205  			case opcode.SYSCALL:
   206  				name, err := interopnames.FromID(GetInteropID(parameter))
   207  				if err != nil {
   208  					name = "not found"
   209  				}
   210  				desc = fmt.Sprintf("%s (%x)", name, parameter)
   211  			case opcode.PUSHINT8, opcode.PUSHINT16, opcode.PUSHINT32,
   212  				opcode.PUSHINT64, opcode.PUSHINT128, opcode.PUSHINT256:
   213  				val := bigint.FromBytes(parameter)
   214  				desc = fmt.Sprintf("%d (%x)", val, parameter)
   215  			case opcode.LDLOC, opcode.STLOC, opcode.LDARG, opcode.STARG, opcode.LDSFLD, opcode.STSFLD:
   216  				desc = fmt.Sprintf("%d (%x)", parameter[0], parameter)
   217  			default:
   218  				if utf8.Valid(parameter) {
   219  					desc = fmt.Sprintf("%x (%q)", parameter, parameter)
   220  				} else {
   221  					// Try converting the parameter to an address and swap the endianness
   222  					// if the parameter is a 20-byte value.
   223  					u, err := util.Uint160DecodeBytesBE(parameter)
   224  					if err == nil {
   225  						desc = fmt.Sprintf("%x (%q, %q)", parameter, address.Uint160ToString(u), "0x"+u.StringLE())
   226  					} else {
   227  						desc = fmt.Sprintf("%x", parameter)
   228  					}
   229  				}
   230  			}
   231  		}
   232  
   233  		fmt.Fprintf(w, "%d\t%s\t%s%s\n", ctx.ip, instr, desc, cursor)
   234  		if ctx.nextip >= len(ctx.sc.prog) {
   235  			break
   236  		}
   237  	}
   238  	w.Flush()
   239  }
   240  
   241  func getOffsetDesc(ctx *Context, parameter []byte) string {
   242  	offset, rOffset, err := calcJumpOffset(ctx, parameter)
   243  	if err != nil {
   244  		return fmt.Sprintf("ERROR: %v", err)
   245  	}
   246  	return fmt.Sprintf("%d (%d/%x)", offset, rOffset, parameter)
   247  }
   248  
   249  // AddBreakPoint adds a breakpoint to the current context.
   250  func (v *VM) AddBreakPoint(n int) {
   251  	ctx := v.Context()
   252  	ctx.sc.breakPoints = append(ctx.sc.breakPoints, n)
   253  }
   254  
   255  // AddBreakPointRel adds a breakpoint relative to the current
   256  // instruction pointer.
   257  func (v *VM) AddBreakPointRel(n int) {
   258  	ctx := v.Context()
   259  	v.AddBreakPoint(ctx.nextip + n)
   260  }
   261  
   262  // LoadFileWithFlags loads a program in NEF format from the given path, ready to execute it.
   263  func (v *VM) LoadFileWithFlags(path string, f callflag.CallFlag) error {
   264  	b, err := os.ReadFile(path)
   265  	if err != nil {
   266  		return err
   267  	}
   268  	nef, err := nef.FileFromBytes(b)
   269  	if err != nil {
   270  		return err
   271  	}
   272  	v.LoadWithFlags(nef.Script, f)
   273  	return nil
   274  }
   275  
   276  // CollectInvocationTree enables collecting invocation tree data.
   277  func (v *VM) EnableInvocationTree() {
   278  	v.invTree = &invocations.Tree{}
   279  }
   280  
   281  // GetInvocationTree returns the current invocation tree structure.
   282  func (v *VM) GetInvocationTree() *invocations.Tree {
   283  	return v.invTree
   284  }
   285  
   286  // Load initializes the VM with the program given.
   287  func (v *VM) Load(prog []byte) {
   288  	v.LoadWithFlags(prog, callflag.NoneFlag)
   289  }
   290  
   291  // LoadWithFlags initializes the VM with the program and flags given.
   292  func (v *VM) LoadWithFlags(prog []byte, f callflag.CallFlag) {
   293  	// Clear all stacks and state, it could be a reload.
   294  	v.istack = v.istack[:0]
   295  	v.estack.Clear()
   296  	v.state = vmstate.None
   297  	v.gasConsumed = 0
   298  	v.invTree = nil
   299  	v.LoadScriptWithFlags(prog, f)
   300  }
   301  
   302  // LoadScript loads a script from the internal script table. It
   303  // will immediately push a new context created from this script to
   304  // the invocation stack and starts executing it.
   305  func (v *VM) LoadScript(b []byte) {
   306  	v.LoadScriptWithFlags(b, callflag.NoneFlag)
   307  }
   308  
   309  // LoadScriptWithFlags loads script and sets call flag to f.
   310  func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
   311  	v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, nil)
   312  }
   313  
   314  // LoadDynamicScript loads the given script with the given flags. This script is
   315  // considered to be dynamic, it can either return no value at all or return
   316  // exactly one value.
   317  func (v *VM) LoadDynamicScript(b []byte, f callflag.CallFlag) {
   318  	v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), util.Uint160{}, f, -1, 0, DynamicOnUnload)
   319  }
   320  
   321  // LoadScriptWithHash is similar to the LoadScriptWithFlags method, but it also loads
   322  // the given script hash directly into the Context to avoid its recalculations and to make
   323  // it possible to override it for deployed contracts with special hashes (the function
   324  // assumes that it is used for deployed contracts setting context's parameters
   325  // accordingly). It's up to the user of this function to make sure the script and hash match
   326  // each other.
   327  func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) {
   328  	v.loadScriptWithCallingHash(b, nil, v.GetCurrentScriptHash(), hash, f, 1, 0, nil)
   329  }
   330  
   331  // LoadNEFMethod allows to create a context to execute a method from the NEF
   332  // file with the specified caller and executing hash, call flags, return value,
   333  // method and _initialize offsets.
   334  func (v *VM) LoadNEFMethod(exe *nef.File, caller util.Uint160, hash util.Uint160, f callflag.CallFlag,
   335  	hasReturn bool, methodOff int, initOff int, onContextUnload ContextUnloadCallback) {
   336  	var rvcount int
   337  	if hasReturn {
   338  		rvcount = 1
   339  	}
   340  	v.loadScriptWithCallingHash(exe.Script, exe, caller, hash, f, rvcount, methodOff, onContextUnload)
   341  	if initOff >= 0 {
   342  		v.Call(initOff)
   343  	}
   344  }
   345  
   346  // loadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly.
   347  // It should be used for calling from native contracts.
   348  func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint160,
   349  	hash util.Uint160, f callflag.CallFlag, rvcount int, offset int, onContextUnload ContextUnloadCallback) {
   350  	v.checkInvocationStackSize()
   351  	ctx := NewContextWithParams(b, rvcount, offset)
   352  	parent := v.Context()
   353  	if parent != nil {
   354  		ctx.sc.callingContext = parent.sc
   355  		parent.sc.estack = v.estack
   356  	}
   357  	if rvcount != -1 || v.estack.Len() != 0 {
   358  		v.estack = subStack(v.estack)
   359  	}
   360  	ctx.sc.estack = v.estack
   361  	initStack(&ctx.tryStack, "exception", nil)
   362  	ctx.sc.callFlag = f
   363  	ctx.sc.scriptHash = hash
   364  	ctx.sc.callingScriptHash = caller
   365  	ctx.sc.NEF = exe
   366  	if v.invTree != nil {
   367  		curTree := v.invTree
   368  		if parent != nil {
   369  			curTree = parent.sc.invTree
   370  		}
   371  		newTree := &invocations.Tree{Current: ctx.ScriptHash()}
   372  		curTree.Calls = append(curTree.Calls, newTree)
   373  		ctx.sc.invTree = newTree
   374  	}
   375  	ctx.sc.onUnload = onContextUnload
   376  	v.istack = append(v.istack, ctx)
   377  }
   378  
   379  // Context returns the current executed context. Nil if there is no context,
   380  // which implies no program is loaded.
   381  func (v *VM) Context() *Context {
   382  	if len(v.istack) == 0 {
   383  		return nil
   384  	}
   385  	return v.istack[len(v.istack)-1]
   386  }
   387  
   388  // PopResult is used to pop the first item of the evaluation stack. This allows
   389  // us to test the compiler and the vm in a bi-directional way.
   390  func (v *VM) PopResult() any {
   391  	if v.estack.Len() == 0 {
   392  		return nil
   393  	}
   394  	return v.estack.Pop().Value()
   395  }
   396  
   397  // DumpIStack returns json formatted representation of the invocation stack.
   398  func (v *VM) DumpIStack() string {
   399  	b, _ := json.MarshalIndent(v.istack, "", "    ")
   400  	return string(b)
   401  }
   402  
   403  // DumpEStack returns json formatted representation of the execution stack.
   404  func (v *VM) DumpEStack() string {
   405  	return dumpStack(v.estack)
   406  }
   407  
   408  // dumpStack returns json formatted representation of the given stack.
   409  func dumpStack(s *Stack) string {
   410  	b, _ := json.MarshalIndent(s, "", "    ")
   411  	return string(b)
   412  }
   413  
   414  // State returns the state for the VM.
   415  func (v *VM) State() vmstate.State {
   416  	return v.state
   417  }
   418  
   419  // Ready returns true if the VM is ready to execute the loaded program.
   420  // It will return false if no program is loaded.
   421  func (v *VM) Ready() bool {
   422  	return len(v.istack) > 0
   423  }
   424  
   425  // Run starts execution of the loaded program.
   426  func (v *VM) Run() error {
   427  	var ctx *Context
   428  
   429  	if !v.Ready() {
   430  		v.state = vmstate.Fault
   431  		return errors.New("no program loaded")
   432  	}
   433  
   434  	if v.state.HasFlag(vmstate.Fault) {
   435  		// VM already ran something and failed, in general its state is
   436  		// undefined in this case so we can't run anything.
   437  		return errors.New("VM has failed")
   438  	}
   439  	// vmstate.Halt (the default) or vmstate.Break are safe to continue.
   440  	v.state = vmstate.None
   441  	ctx = v.Context()
   442  	for {
   443  		switch {
   444  		case v.state.HasFlag(vmstate.Fault):
   445  			// Should be caught and reported already by the v.Step(),
   446  			// but we're checking here anyway just in case.
   447  			return errors.New("VM has failed")
   448  		case v.state.HasFlag(vmstate.Halt), v.state.HasFlag(vmstate.Break):
   449  			// Normal exit from this loop.
   450  			return nil
   451  		case v.state == vmstate.None:
   452  			if err := v.step(ctx); err != nil {
   453  				return err
   454  			}
   455  		default:
   456  			v.state = vmstate.Fault
   457  			return errors.New("unknown state")
   458  		}
   459  		// check for breakpoint before executing the next instruction
   460  		ctx = v.Context()
   461  		if ctx != nil && ctx.atBreakPoint() {
   462  			v.state = vmstate.Break
   463  		}
   464  	}
   465  }
   466  
   467  // Step 1 instruction in the program.
   468  func (v *VM) Step() error {
   469  	ctx := v.Context()
   470  	return v.step(ctx)
   471  }
   472  
   473  // step executes one instruction in the given context.
   474  func (v *VM) step(ctx *Context) error {
   475  	op, param, err := ctx.Next()
   476  	if err != nil {
   477  		v.state = vmstate.Fault
   478  		return newError(ctx.ip, op, err)
   479  	}
   480  	return v.execute(ctx, op, param)
   481  }
   482  
   483  // StepInto behaves the same as “step over” in case the line does not contain a function. Otherwise,
   484  // the debugger will enter the called function and continue line-by-line debugging there.
   485  func (v *VM) StepInto() error {
   486  	ctx := v.Context()
   487  
   488  	if ctx == nil {
   489  		v.state = vmstate.Halt
   490  	}
   491  
   492  	if v.HasStopped() {
   493  		return nil
   494  	}
   495  
   496  	if ctx != nil && ctx.sc.prog != nil {
   497  		op, param, err := ctx.Next()
   498  		if err != nil {
   499  			v.state = vmstate.Fault
   500  			return newError(ctx.ip, op, err)
   501  		}
   502  		vErr := v.execute(ctx, op, param)
   503  		if vErr != nil {
   504  			return vErr
   505  		}
   506  	}
   507  
   508  	cctx := v.Context()
   509  	if cctx != nil && cctx.atBreakPoint() {
   510  		v.state = vmstate.Break
   511  	}
   512  	return nil
   513  }
   514  
   515  // StepOut takes the debugger to the line where the current function was called.
   516  func (v *VM) StepOut() error {
   517  	var err error
   518  	if v.state == vmstate.Break {
   519  		v.state = vmstate.None
   520  	}
   521  
   522  	expSize := len(v.istack)
   523  	for v.state == vmstate.None && len(v.istack) >= expSize {
   524  		err = v.StepInto()
   525  	}
   526  	if v.state == vmstate.None {
   527  		v.state = vmstate.Break
   528  	}
   529  	return err
   530  }
   531  
   532  // StepOver takes the debugger to the line that will step over the given line.
   533  // If the line contains a function, the function will be executed and the result is returned without debugging each line.
   534  func (v *VM) StepOver() error {
   535  	var err error
   536  	if v.HasStopped() {
   537  		return err
   538  	}
   539  
   540  	if v.state == vmstate.Break {
   541  		v.state = vmstate.None
   542  	}
   543  
   544  	expSize := len(v.istack)
   545  	for {
   546  		err = v.StepInto()
   547  		if !(v.state == vmstate.None && len(v.istack) > expSize) {
   548  			break
   549  		}
   550  	}
   551  
   552  	if v.state == vmstate.None {
   553  		v.state = vmstate.Break
   554  	}
   555  
   556  	return err
   557  }
   558  
   559  // HasFailed returns whether the VM is in the failed state now. Usually, it's used to
   560  // check status after Run.
   561  func (v *VM) HasFailed() bool {
   562  	return v.state.HasFlag(vmstate.Fault)
   563  }
   564  
   565  // HasStopped returns whether the VM is in the Halt or Failed state.
   566  func (v *VM) HasStopped() bool {
   567  	return v.state.HasFlag(vmstate.Halt) || v.state.HasFlag(vmstate.Fault)
   568  }
   569  
   570  // HasHalted returns whether the VM is in the Halt state.
   571  func (v *VM) HasHalted() bool {
   572  	return v.state.HasFlag(vmstate.Halt)
   573  }
   574  
   575  // AtBreakpoint returns whether the VM is at breakpoint.
   576  func (v *VM) AtBreakpoint() bool {
   577  	return v.state.HasFlag(vmstate.Break)
   578  }
   579  
   580  // GetInteropID converts instruction parameter to an interop ID.
   581  func GetInteropID(parameter []byte) uint32 {
   582  	return binary.LittleEndian.Uint32(parameter)
   583  }
   584  
   585  // execute performs an instruction cycle in the VM. Acting on the instruction (opcode).
   586  func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err error) {
   587  	// Instead of polluting the whole VM logic with error handling, we will recover
   588  	// each panic at a central point, putting the VM in a fault state and setting error.
   589  	defer func() {
   590  		if errRecover := recover(); errRecover != nil {
   591  			v.state = vmstate.Fault
   592  			err = newError(ctx.ip, op, errRecover)
   593  		} else if v.refs > MaxStackSize {
   594  			v.state = vmstate.Fault
   595  			err = newError(ctx.ip, op, "stack is too big")
   596  		}
   597  	}()
   598  
   599  	if v.getPrice != nil && ctx.ip < len(ctx.sc.prog) {
   600  		v.gasConsumed += v.getPrice(op, parameter)
   601  		if v.GasLimit >= 0 && v.gasConsumed > v.GasLimit {
   602  			panic("gas limit is exceeded")
   603  		}
   604  	}
   605  
   606  	if op <= opcode.PUSHINT256 {
   607  		v.estack.PushItem(stackitem.NewBigInteger(bigint.FromBytes(parameter)))
   608  		return
   609  	}
   610  
   611  	switch op {
   612  	case opcode.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3,
   613  		opcode.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7,
   614  		opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11,
   615  		opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15,
   616  		opcode.PUSH16:
   617  		val := int(op) - int(opcode.PUSH0)
   618  		v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(val))))
   619  
   620  	case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4:
   621  		v.estack.PushItem(stackitem.NewByteArray(parameter))
   622  
   623  	case opcode.PUSHT, opcode.PUSHF:
   624  		v.estack.PushItem(stackitem.NewBool(op == opcode.PUSHT))
   625  
   626  	case opcode.PUSHA:
   627  		n := getJumpOffset(ctx, parameter)
   628  		ptr := stackitem.NewPointerWithHash(n, ctx.sc.prog, ctx.ScriptHash())
   629  		v.estack.PushItem(ptr)
   630  
   631  	case opcode.PUSHNULL:
   632  		v.estack.PushItem(stackitem.Null{})
   633  
   634  	case opcode.ISNULL:
   635  		_, ok := v.estack.Pop().value.(stackitem.Null)
   636  		v.estack.PushItem(stackitem.Bool(ok))
   637  
   638  	case opcode.ISTYPE:
   639  		res := v.estack.Pop().Item()
   640  		v.estack.PushItem(stackitem.Bool(res.Type() == stackitem.Type(parameter[0])))
   641  
   642  	case opcode.CONVERT:
   643  		typ := stackitem.Type(parameter[0])
   644  		item := v.estack.Pop().Item()
   645  		result, err := item.Convert(typ)
   646  		if err != nil {
   647  			panic(err)
   648  		}
   649  		v.estack.PushItem(result)
   650  
   651  	case opcode.INITSSLOT:
   652  		if parameter[0] == 0 {
   653  			panic("zero argument")
   654  		}
   655  		ctx.sc.static.init(int(parameter[0]), &v.refs)
   656  
   657  	case opcode.INITSLOT:
   658  		if ctx.local != nil || ctx.arguments != nil {
   659  			panic("already initialized")
   660  		}
   661  		if parameter[0] == 0 && parameter[1] == 0 {
   662  			panic("zero argument")
   663  		}
   664  		if parameter[0] > 0 {
   665  			ctx.local.init(int(parameter[0]), &v.refs)
   666  		}
   667  		if parameter[1] > 0 {
   668  			sz := int(parameter[1])
   669  			ctx.arguments.init(sz, &v.refs)
   670  			for i := 0; i < sz; i++ {
   671  				ctx.arguments.Set(i, v.estack.Pop().Item(), &v.refs)
   672  			}
   673  		}
   674  
   675  	case opcode.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6:
   676  		item := ctx.sc.static.Get(int(op - opcode.LDSFLD0))
   677  		v.estack.PushItem(item)
   678  
   679  	case opcode.LDSFLD:
   680  		item := ctx.sc.static.Get(int(parameter[0]))
   681  		v.estack.PushItem(item)
   682  
   683  	case opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode.STSFLD5, opcode.STSFLD6:
   684  		item := v.estack.Pop().Item()
   685  		ctx.sc.static.Set(int(op-opcode.STSFLD0), item, &v.refs)
   686  
   687  	case opcode.STSFLD:
   688  		item := v.estack.Pop().Item()
   689  		ctx.sc.static.Set(int(parameter[0]), item, &v.refs)
   690  
   691  	case opcode.LDLOC0, opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode.LDLOC6:
   692  		item := ctx.local.Get(int(op - opcode.LDLOC0))
   693  		v.estack.PushItem(item)
   694  
   695  	case opcode.LDLOC:
   696  		item := ctx.local.Get(int(parameter[0]))
   697  		v.estack.PushItem(item)
   698  
   699  	case opcode.STLOC0, opcode.STLOC1, opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6:
   700  		item := v.estack.Pop().Item()
   701  		ctx.local.Set(int(op-opcode.STLOC0), item, &v.refs)
   702  
   703  	case opcode.STLOC:
   704  		item := v.estack.Pop().Item()
   705  		ctx.local.Set(int(parameter[0]), item, &v.refs)
   706  
   707  	case opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6:
   708  		item := ctx.arguments.Get(int(op - opcode.LDARG0))
   709  		v.estack.PushItem(item)
   710  
   711  	case opcode.LDARG:
   712  		item := ctx.arguments.Get(int(parameter[0]))
   713  		v.estack.PushItem(item)
   714  
   715  	case opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4, opcode.STARG5, opcode.STARG6:
   716  		item := v.estack.Pop().Item()
   717  		ctx.arguments.Set(int(op-opcode.STARG0), item, &v.refs)
   718  
   719  	case opcode.STARG:
   720  		item := v.estack.Pop().Item()
   721  		ctx.arguments.Set(int(parameter[0]), item, &v.refs)
   722  
   723  	case opcode.NEWBUFFER:
   724  		n := toInt(v.estack.Pop().BigInt())
   725  		if n < 0 || n > stackitem.MaxSize {
   726  			panic("invalid size")
   727  		}
   728  		v.estack.PushItem(stackitem.NewBuffer(make([]byte, n)))
   729  
   730  	case opcode.MEMCPY:
   731  		n := toInt(v.estack.Pop().BigInt())
   732  		if n < 0 {
   733  			panic("invalid size")
   734  		}
   735  		si := toInt(v.estack.Pop().BigInt())
   736  		if si < 0 {
   737  			panic("invalid source index")
   738  		}
   739  		src := v.estack.Pop().Bytes()
   740  		if sum := si + n; sum < 0 || sum > len(src) {
   741  			panic("size is too big")
   742  		}
   743  		di := toInt(v.estack.Pop().BigInt())
   744  		if di < 0 {
   745  			panic("invalid destination index")
   746  		}
   747  		dst := v.estack.Pop().value.(*stackitem.Buffer).Value().([]byte)
   748  		if sum := di + n; sum < 0 || sum > len(dst) {
   749  			panic("size is too big")
   750  		}
   751  		copy(dst[di:], src[si:si+n])
   752  
   753  	case opcode.CAT:
   754  		b := v.estack.Pop().Bytes()
   755  		a := v.estack.Pop().Bytes()
   756  		l := len(a) + len(b)
   757  		if l > stackitem.MaxSize {
   758  			panic(fmt.Sprintf("too big item: %d", l))
   759  		}
   760  		ab := make([]byte, l)
   761  		copy(ab, a)
   762  		copy(ab[len(a):], b)
   763  		v.estack.PushItem(stackitem.NewBuffer(ab))
   764  
   765  	case opcode.SUBSTR:
   766  		l := toInt(v.estack.Pop().BigInt())
   767  		if l < 0 {
   768  			panic("negative length")
   769  		}
   770  		o := toInt(v.estack.Pop().BigInt())
   771  		if o < 0 {
   772  			panic("negative index")
   773  		}
   774  		s := v.estack.Pop().Bytes()
   775  		last := l + o
   776  		if last > len(s) {
   777  			panic("invalid offset")
   778  		}
   779  		res := make([]byte, l)
   780  		copy(res, s[o:last])
   781  		v.estack.PushItem(stackitem.NewBuffer(res))
   782  
   783  	case opcode.LEFT:
   784  		l := toInt(v.estack.Pop().BigInt())
   785  		if l < 0 {
   786  			panic("negative length")
   787  		}
   788  		s := v.estack.Pop().Bytes()
   789  		if t := len(s); l > t {
   790  			panic("size is too big")
   791  		}
   792  		res := make([]byte, l)
   793  		copy(res, s[:l])
   794  		v.estack.PushItem(stackitem.NewBuffer(res))
   795  
   796  	case opcode.RIGHT:
   797  		l := toInt(v.estack.Pop().BigInt())
   798  		if l < 0 {
   799  			panic("negative length")
   800  		}
   801  		s := v.estack.Pop().Bytes()
   802  		res := make([]byte, l)
   803  		copy(res, s[len(s)-l:])
   804  		v.estack.PushItem(stackitem.NewBuffer(res))
   805  
   806  	case opcode.DEPTH:
   807  		v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(v.estack.Len()))))
   808  
   809  	case opcode.DROP:
   810  		if v.estack.Len() < 1 {
   811  			panic("stack is too small")
   812  		}
   813  		v.estack.Pop()
   814  
   815  	case opcode.NIP:
   816  		if v.estack.Len() < 2 {
   817  			panic("no second element found")
   818  		}
   819  		_ = v.estack.RemoveAt(1)
   820  
   821  	case opcode.XDROP:
   822  		n := toInt(v.estack.Pop().BigInt())
   823  		if n < 0 {
   824  			panic("invalid length")
   825  		}
   826  		if v.estack.Len() < n+1 {
   827  			panic("bad index")
   828  		}
   829  		_ = v.estack.RemoveAt(n)
   830  
   831  	case opcode.CLEAR:
   832  		v.estack.Clear()
   833  
   834  	case opcode.DUP:
   835  		v.estack.Push(v.estack.Dup(0))
   836  
   837  	case opcode.OVER:
   838  		if v.estack.Len() < 2 {
   839  			panic("no second element found")
   840  		}
   841  		a := v.estack.Dup(1)
   842  		v.estack.Push(a)
   843  
   844  	case opcode.PICK:
   845  		n := toInt(v.estack.Pop().BigInt())
   846  		if n < 0 {
   847  			panic("negative stack item returned")
   848  		}
   849  		if v.estack.Len() < n+1 {
   850  			panic("no nth element found")
   851  		}
   852  		a := v.estack.Dup(n)
   853  		v.estack.Push(a)
   854  
   855  	case opcode.TUCK:
   856  		if v.estack.Len() < 2 {
   857  			panic("too short stack to TUCK")
   858  		}
   859  		a := v.estack.Dup(0)
   860  		v.estack.InsertAt(a, 2)
   861  
   862  	case opcode.SWAP:
   863  		err := v.estack.Swap(1, 0)
   864  		if err != nil {
   865  			panic(err.Error())
   866  		}
   867  
   868  	case opcode.ROT:
   869  		err := v.estack.Roll(2)
   870  		if err != nil {
   871  			panic(err.Error())
   872  		}
   873  
   874  	case opcode.ROLL:
   875  		n := toInt(v.estack.Pop().BigInt())
   876  		err := v.estack.Roll(n)
   877  		if err != nil {
   878  			panic(err.Error())
   879  		}
   880  
   881  	case opcode.REVERSE3, opcode.REVERSE4, opcode.REVERSEN:
   882  		n := 3
   883  		switch op {
   884  		case opcode.REVERSE4:
   885  			n = 4
   886  		case opcode.REVERSEN:
   887  			n = toInt(v.estack.Pop().BigInt())
   888  		}
   889  		if err := v.estack.ReverseTop(n); err != nil {
   890  			panic(err.Error())
   891  		}
   892  
   893  	// Bit operations.
   894  	case opcode.INVERT:
   895  		i := v.estack.Pop().BigInt()
   896  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Not(i)))
   897  
   898  	case opcode.AND:
   899  		b := v.estack.Pop().BigInt()
   900  		a := v.estack.Pop().BigInt()
   901  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).And(b, a)))
   902  
   903  	case opcode.OR:
   904  		b := v.estack.Pop().BigInt()
   905  		a := v.estack.Pop().BigInt()
   906  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Or(b, a)))
   907  
   908  	case opcode.XOR:
   909  		b := v.estack.Pop().BigInt()
   910  		a := v.estack.Pop().BigInt()
   911  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Xor(b, a)))
   912  
   913  	case opcode.EQUAL, opcode.NOTEQUAL:
   914  		if v.estack.Len() < 2 {
   915  			panic("need a pair of elements on the stack")
   916  		}
   917  		b := v.estack.Pop()
   918  		a := v.estack.Pop()
   919  		res := stackitem.Bool(a.value.Equals(b.value) == (op == opcode.EQUAL))
   920  		v.estack.PushItem(res)
   921  
   922  	// Numeric operations.
   923  	case opcode.SIGN:
   924  		x := v.estack.Pop().BigInt()
   925  		v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(x.Sign()))))
   926  
   927  	case opcode.ABS:
   928  		x := v.estack.Pop().BigInt()
   929  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Abs(x)))
   930  
   931  	case opcode.NEGATE:
   932  		x := v.estack.Pop().BigInt()
   933  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Neg(x)))
   934  
   935  	case opcode.INC:
   936  		x := v.estack.Pop().BigInt()
   937  		a := new(big.Int).Add(x, bigOne)
   938  		v.estack.PushItem(stackitem.NewBigInteger(a))
   939  
   940  	case opcode.DEC:
   941  		x := v.estack.Pop().BigInt()
   942  		a := new(big.Int).Sub(x, bigOne)
   943  		v.estack.PushItem(stackitem.NewBigInteger(a))
   944  
   945  	case opcode.ADD:
   946  		a := v.estack.Pop().BigInt()
   947  		b := v.estack.Pop().BigInt()
   948  
   949  		c := new(big.Int).Add(a, b)
   950  		v.estack.PushItem(stackitem.NewBigInteger(c))
   951  
   952  	case opcode.SUB:
   953  		b := v.estack.Pop().BigInt()
   954  		a := v.estack.Pop().BigInt()
   955  
   956  		c := new(big.Int).Sub(a, b)
   957  		v.estack.PushItem(stackitem.NewBigInteger(c))
   958  
   959  	case opcode.MUL:
   960  		a := v.estack.Pop().BigInt()
   961  		b := v.estack.Pop().BigInt()
   962  
   963  		c := new(big.Int).Mul(a, b)
   964  		v.estack.PushItem(stackitem.NewBigInteger(c))
   965  
   966  	case opcode.DIV:
   967  		b := v.estack.Pop().BigInt()
   968  		a := v.estack.Pop().BigInt()
   969  
   970  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Quo(a, b)))
   971  
   972  	case opcode.MOD:
   973  		b := v.estack.Pop().BigInt()
   974  		a := v.estack.Pop().BigInt()
   975  
   976  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Rem(a, b)))
   977  
   978  	case opcode.POW:
   979  		exp := v.estack.Pop().BigInt()
   980  		a := v.estack.Pop().BigInt()
   981  		if ei := exp.Uint64(); !exp.IsUint64() || ei > maxSHLArg {
   982  			panic("invalid exponent")
   983  		}
   984  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Exp(a, exp, nil)))
   985  
   986  	case opcode.SQRT:
   987  		a := v.estack.Pop().BigInt()
   988  		if a.Sign() == -1 {
   989  			panic("negative value")
   990  		}
   991  
   992  		v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a)))
   993  
   994  	case opcode.MODMUL:
   995  		modulus := v.estack.Pop().BigInt()
   996  		if modulus.Sign() == 0 {
   997  			panic("zero modulus")
   998  		}
   999  		x2 := v.estack.Pop().BigInt()
  1000  		x1 := v.estack.Pop().BigInt()
  1001  
  1002  		res := new(big.Int).Mul(x1, x2)
  1003  		v.estack.PushItem(stackitem.NewBigInteger(res.Mod(res, modulus)))
  1004  
  1005  	case opcode.MODPOW:
  1006  		modulus := v.estack.Pop().BigInt()
  1007  		exponent := v.estack.Pop().BigInt()
  1008  		base := v.estack.Pop().BigInt()
  1009  		res := new(big.Int)
  1010  		switch exponent.Cmp(bigMinusOne) {
  1011  		case -1:
  1012  			panic("exponent should be >= -1")
  1013  		case 0:
  1014  			if base.Cmp(bigZero) <= 0 {
  1015  				panic("invalid base")
  1016  			}
  1017  			if modulus.Cmp(bigTwo) < 0 {
  1018  				panic("invalid modulus")
  1019  			}
  1020  			if res.ModInverse(base, modulus) == nil {
  1021  				panic("base and modulus are not relatively prime")
  1022  			}
  1023  		case 1:
  1024  			if modulus.Sign() == 0 {
  1025  				panic("zero modulus") // https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger.modpow?view=net-6.0#exceptions
  1026  			}
  1027  			res.Exp(base, exponent, modulus)
  1028  		}
  1029  
  1030  		v.estack.PushItem(stackitem.NewBigInteger(res))
  1031  
  1032  	case opcode.SHL, opcode.SHR:
  1033  		b := toInt(v.estack.Pop().BigInt())
  1034  		if b == 0 {
  1035  			return
  1036  		} else if b < 0 || b > maxSHLArg {
  1037  			panic(fmt.Sprintf("operand must be between %d and %d", 0, maxSHLArg))
  1038  		}
  1039  		a := v.estack.Pop().BigInt()
  1040  
  1041  		var item big.Int
  1042  		if op == opcode.SHL {
  1043  			item.Lsh(a, uint(b))
  1044  		} else {
  1045  			item.Rsh(a, uint(b))
  1046  		}
  1047  
  1048  		v.estack.PushItem(stackitem.NewBigInteger(&item))
  1049  
  1050  	case opcode.NOT:
  1051  		x := v.estack.Pop().Bool()
  1052  		v.estack.PushItem(stackitem.Bool(!x))
  1053  
  1054  	case opcode.BOOLAND:
  1055  		b := v.estack.Pop().Bool()
  1056  		a := v.estack.Pop().Bool()
  1057  		v.estack.PushItem(stackitem.Bool(a && b))
  1058  
  1059  	case opcode.BOOLOR:
  1060  		b := v.estack.Pop().Bool()
  1061  		a := v.estack.Pop().Bool()
  1062  		v.estack.PushItem(stackitem.Bool(a || b))
  1063  
  1064  	case opcode.NZ:
  1065  		x := v.estack.Pop().BigInt()
  1066  		v.estack.PushItem(stackitem.Bool(x.Sign() != 0))
  1067  
  1068  	case opcode.NUMEQUAL:
  1069  		b := v.estack.Pop().BigInt()
  1070  		a := v.estack.Pop().BigInt()
  1071  		v.estack.PushItem(stackitem.Bool(a.Cmp(b) == 0))
  1072  
  1073  	case opcode.NUMNOTEQUAL:
  1074  		b := v.estack.Pop().BigInt()
  1075  		a := v.estack.Pop().BigInt()
  1076  		v.estack.PushItem(stackitem.Bool(a.Cmp(b) != 0))
  1077  
  1078  	case opcode.LT, opcode.LE, opcode.GT, opcode.GE:
  1079  		eb := v.estack.Pop()
  1080  		ea := v.estack.Pop()
  1081  		_, aNil := ea.Item().(stackitem.Null)
  1082  		_, bNil := eb.Item().(stackitem.Null)
  1083  
  1084  		res := !aNil && !bNil
  1085  		if res {
  1086  			cmp := ea.BigInt().Cmp(eb.BigInt())
  1087  			switch op {
  1088  			case opcode.LT:
  1089  				res = cmp == -1
  1090  			case opcode.LE:
  1091  				res = cmp <= 0
  1092  			case opcode.GT:
  1093  				res = cmp == 1
  1094  			case opcode.GE:
  1095  				res = cmp >= 0
  1096  			}
  1097  		}
  1098  		v.estack.PushItem(stackitem.Bool(res))
  1099  
  1100  	case opcode.MIN:
  1101  		b := v.estack.Pop().BigInt()
  1102  		a := v.estack.Pop().BigInt()
  1103  		val := a
  1104  		if a.Cmp(b) == 1 {
  1105  			val = b
  1106  		}
  1107  		v.estack.PushItem(stackitem.NewBigInteger(val))
  1108  
  1109  	case opcode.MAX:
  1110  		b := v.estack.Pop().BigInt()
  1111  		a := v.estack.Pop().BigInt()
  1112  		val := a
  1113  		if a.Cmp(b) == -1 {
  1114  			val = b
  1115  		}
  1116  		v.estack.PushItem(stackitem.NewBigInteger(val))
  1117  
  1118  	case opcode.WITHIN:
  1119  		b := v.estack.Pop().BigInt()
  1120  		a := v.estack.Pop().BigInt()
  1121  		x := v.estack.Pop().BigInt()
  1122  		v.estack.PushItem(stackitem.Bool(a.Cmp(x) <= 0 && x.Cmp(b) == -1))
  1123  
  1124  	// Object operations
  1125  	case opcode.NEWARRAY0:
  1126  		v.estack.PushItem(stackitem.NewArray([]stackitem.Item{}))
  1127  
  1128  	case opcode.NEWARRAY, opcode.NEWARRAYT, opcode.NEWSTRUCT:
  1129  		n := toInt(v.estack.Pop().BigInt())
  1130  		if n < 0 || n > MaxStackSize {
  1131  			panic("wrong number of elements")
  1132  		}
  1133  		typ := stackitem.AnyT
  1134  		if op == opcode.NEWARRAYT {
  1135  			typ = stackitem.Type(parameter[0])
  1136  		}
  1137  		items := makeArrayOfType(int(n), typ)
  1138  		var res stackitem.Item
  1139  		if op == opcode.NEWSTRUCT {
  1140  			res = stackitem.NewStruct(items)
  1141  		} else {
  1142  			res = stackitem.NewArray(items)
  1143  		}
  1144  		v.estack.PushItem(res)
  1145  
  1146  	case opcode.NEWSTRUCT0:
  1147  		v.estack.PushItem(stackitem.NewStruct([]stackitem.Item{}))
  1148  
  1149  	case opcode.APPEND:
  1150  		itemElem := v.estack.Pop()
  1151  		arrElem := v.estack.Pop()
  1152  
  1153  		val := cloneIfStruct(itemElem.value)
  1154  
  1155  		switch t := arrElem.value.(type) {
  1156  		case *stackitem.Array:
  1157  			t.Append(val)
  1158  		case *stackitem.Struct:
  1159  			t.Append(val)
  1160  		default:
  1161  			panic("APPEND: not of underlying type Array")
  1162  		}
  1163  
  1164  		v.refs.Add(val)
  1165  
  1166  	case opcode.PACKMAP:
  1167  		n := toInt(v.estack.Pop().BigInt())
  1168  		if n < 0 || n*2 > v.estack.Len() {
  1169  			panic("invalid length")
  1170  		}
  1171  
  1172  		items := make([]stackitem.MapElement, n)
  1173  		for i := 0; i < n; i++ {
  1174  			key := v.estack.Pop()
  1175  			validateMapKey(key)
  1176  			val := v.estack.Pop().value
  1177  			items[i].Key = key.value
  1178  			items[i].Value = val
  1179  		}
  1180  		v.estack.PushItem(stackitem.NewMapWithValue(items))
  1181  
  1182  	case opcode.PACKSTRUCT, opcode.PACK:
  1183  		n := toInt(v.estack.Pop().BigInt())
  1184  		if n < 0 || n > v.estack.Len() {
  1185  			panic("OPACK: invalid length")
  1186  		}
  1187  
  1188  		items := make([]stackitem.Item, n)
  1189  		for i := 0; i < n; i++ {
  1190  			items[i] = v.estack.Pop().value
  1191  		}
  1192  
  1193  		var res stackitem.Item
  1194  		if op == opcode.PACK {
  1195  			res = stackitem.NewArray(items)
  1196  		} else {
  1197  			res = stackitem.NewStruct(items)
  1198  		}
  1199  		v.estack.PushItem(res)
  1200  
  1201  	case opcode.UNPACK:
  1202  		e := v.estack.Pop()
  1203  		var arr []stackitem.Item
  1204  		var l int
  1205  
  1206  		switch t := e.value.(type) {
  1207  		case *stackitem.Array:
  1208  			arr = t.Value().([]stackitem.Item)
  1209  		case *stackitem.Struct:
  1210  			arr = t.Value().([]stackitem.Item)
  1211  		case *stackitem.Map:
  1212  			m := t.Value().([]stackitem.MapElement)
  1213  			l = len(m)
  1214  			for i := l - 1; i >= 0; i-- {
  1215  				v.estack.PushItem(m[i].Value)
  1216  				v.estack.PushItem(m[i].Key)
  1217  			}
  1218  		default:
  1219  			panic("element is not an array/struct/map")
  1220  		}
  1221  		if arr != nil {
  1222  			l = len(arr)
  1223  			for i := l - 1; i >= 0; i-- {
  1224  				v.estack.PushItem(arr[i])
  1225  			}
  1226  		}
  1227  		v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(l))))
  1228  
  1229  	case opcode.PICKITEM:
  1230  		key := v.estack.Pop()
  1231  		validateMapKey(key)
  1232  
  1233  		obj := v.estack.Pop()
  1234  
  1235  		switch t := obj.value.(type) {
  1236  		// Struct and Array items have their underlying value as []Item.
  1237  		case *stackitem.Array, *stackitem.Struct:
  1238  			index := toInt(key.BigInt())
  1239  			arr := t.Value().([]stackitem.Item)
  1240  			if index < 0 || index >= len(arr) {
  1241  				msg := fmt.Sprintf("The value %d is out of range.", index)
  1242  				v.throw(stackitem.NewByteArray([]byte(msg)))
  1243  				return
  1244  			}
  1245  			item := arr[index].Dup()
  1246  			v.estack.PushItem(item)
  1247  		case *stackitem.Map:
  1248  			index := t.Index(key.Item())
  1249  			if index < 0 {
  1250  				v.throw(stackitem.NewByteArray([]byte("Key not found in Map")))
  1251  				return
  1252  			}
  1253  			v.estack.PushItem(t.Value().([]stackitem.MapElement)[index].Value.Dup())
  1254  		default:
  1255  			index := toInt(key.BigInt())
  1256  			arr := obj.Bytes()
  1257  			if index < 0 || index >= len(arr) {
  1258  				msg := fmt.Sprintf("The value %d is out of range.", index)
  1259  				v.throw(stackitem.NewByteArray([]byte(msg)))
  1260  				return
  1261  			}
  1262  			item := arr[index]
  1263  			v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(item))))
  1264  		}
  1265  
  1266  	case opcode.SETITEM:
  1267  		item := v.estack.Pop().value
  1268  		key := v.estack.Pop()
  1269  		validateMapKey(key)
  1270  
  1271  		obj := v.estack.Pop()
  1272  
  1273  		switch t := obj.value.(type) {
  1274  		// Struct and Array items have their underlying value as []Item.
  1275  		case *stackitem.Array, *stackitem.Struct:
  1276  			arr := t.Value().([]stackitem.Item)
  1277  			index := toInt(key.BigInt())
  1278  			if index < 0 || index >= len(arr) {
  1279  				msg := fmt.Sprintf("The value %d is out of range.", index)
  1280  				v.throw(stackitem.NewByteArray([]byte(msg)))
  1281  				return
  1282  			}
  1283  			if t.(stackitem.Immutable).IsReadOnly() {
  1284  				panic(stackitem.ErrReadOnly)
  1285  			}
  1286  			v.refs.Remove(arr[index])
  1287  			arr[index] = item
  1288  			v.refs.Add(arr[index])
  1289  		case *stackitem.Map:
  1290  			if t.IsReadOnly() {
  1291  				panic(stackitem.ErrReadOnly)
  1292  			}
  1293  			if i := t.Index(key.value); i >= 0 {
  1294  				v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
  1295  			} else {
  1296  				v.refs.Add(key.value)
  1297  			}
  1298  			t.Add(key.value, item)
  1299  			v.refs.Add(item)
  1300  
  1301  		case *stackitem.Buffer:
  1302  			index := toInt(key.BigInt())
  1303  			if index < 0 || index >= t.Len() {
  1304  				msg := fmt.Sprintf("The value %d is out of range.", index)
  1305  				v.throw(stackitem.NewByteArray([]byte(msg)))
  1306  				return
  1307  			}
  1308  			bi, err := item.TryInteger()
  1309  			b := toInt(bi)
  1310  			if err != nil || b < math.MinInt8 || b > math.MaxUint8 {
  1311  				panic("invalid value")
  1312  			}
  1313  			t.Value().([]byte)[index] = byte(b)
  1314  
  1315  		default:
  1316  			panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
  1317  		}
  1318  
  1319  	case opcode.REVERSEITEMS:
  1320  		item := v.estack.Pop()
  1321  		switch t := item.value.(type) {
  1322  		case *stackitem.Array, *stackitem.Struct:
  1323  			if t.(stackitem.Immutable).IsReadOnly() {
  1324  				panic(stackitem.ErrReadOnly)
  1325  			}
  1326  			a := t.Value().([]stackitem.Item)
  1327  			for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
  1328  				a[i], a[j] = a[j], a[i]
  1329  			}
  1330  		case *stackitem.Buffer:
  1331  			b := t.Value().([]byte)
  1332  			slice.Reverse(b)
  1333  		default:
  1334  			panic(fmt.Sprintf("invalid item type %s", t))
  1335  		}
  1336  	case opcode.REMOVE:
  1337  		key := v.estack.Pop()
  1338  		validateMapKey(key)
  1339  
  1340  		elem := v.estack.Pop()
  1341  		switch t := elem.value.(type) {
  1342  		case *stackitem.Array:
  1343  			a := t.Value().([]stackitem.Item)
  1344  			k := toInt(key.BigInt())
  1345  			if k < 0 || k >= len(a) {
  1346  				panic("REMOVE: invalid index")
  1347  			}
  1348  			toRemove := a[k]
  1349  			t.Remove(k)
  1350  			v.refs.Remove(toRemove)
  1351  		case *stackitem.Struct:
  1352  			a := t.Value().([]stackitem.Item)
  1353  			k := toInt(key.BigInt())
  1354  			if k < 0 || k >= len(a) {
  1355  				panic("REMOVE: invalid index")
  1356  			}
  1357  			toRemove := a[k]
  1358  			t.Remove(k)
  1359  			v.refs.Remove(toRemove)
  1360  		case *stackitem.Map:
  1361  			index := t.Index(key.Item())
  1362  			// No error on missing key.
  1363  			if index >= 0 {
  1364  				elems := t.Value().([]stackitem.MapElement)
  1365  				key := elems[index].Key
  1366  				val := elems[index].Value
  1367  				t.Drop(index)
  1368  				v.refs.Remove(key)
  1369  				v.refs.Remove(val)
  1370  			}
  1371  		default:
  1372  			panic("REMOVE: invalid type")
  1373  		}
  1374  
  1375  	case opcode.CLEARITEMS:
  1376  		elem := v.estack.Pop()
  1377  		switch t := elem.value.(type) {
  1378  		case *stackitem.Array:
  1379  			if t.IsReadOnly() {
  1380  				panic(stackitem.ErrReadOnly)
  1381  			}
  1382  			for _, item := range t.Value().([]stackitem.Item) {
  1383  				v.refs.Remove(item)
  1384  			}
  1385  			t.Clear()
  1386  		case *stackitem.Struct:
  1387  			if t.IsReadOnly() {
  1388  				panic(stackitem.ErrReadOnly)
  1389  			}
  1390  			for _, item := range t.Value().([]stackitem.Item) {
  1391  				v.refs.Remove(item)
  1392  			}
  1393  			t.Clear()
  1394  		case *stackitem.Map:
  1395  			if t.IsReadOnly() {
  1396  				panic(stackitem.ErrReadOnly)
  1397  			}
  1398  			elems := t.Value().([]stackitem.MapElement)
  1399  			for i := range elems {
  1400  				v.refs.Remove(elems[i].Key)
  1401  				v.refs.Remove(elems[i].Value)
  1402  			}
  1403  			t.Clear()
  1404  		default:
  1405  			panic("CLEARITEMS: invalid type")
  1406  		}
  1407  
  1408  	case opcode.POPITEM:
  1409  		arr := v.estack.Pop().Item()
  1410  		elems := arr.Value().([]stackitem.Item)
  1411  		index := len(elems) - 1
  1412  		elem := elems[index]
  1413  		v.estack.PushItem(elem) // push item on stack firstly, to match the reference behaviour.
  1414  		switch item := arr.(type) {
  1415  		case *stackitem.Array:
  1416  			item.Remove(index)
  1417  		case *stackitem.Struct:
  1418  			item.Remove(index)
  1419  		}
  1420  		v.refs.Remove(elem)
  1421  
  1422  	case opcode.SIZE:
  1423  		elem := v.estack.Pop()
  1424  		var res int
  1425  		// Cause there is no native (byte) item type here, we need to check
  1426  		// the type of the item for array size operations.
  1427  		switch t := elem.Value().(type) {
  1428  		case []stackitem.Item:
  1429  			res = len(t)
  1430  		case []stackitem.MapElement:
  1431  			res = len(t)
  1432  		default:
  1433  			res = len(elem.Bytes())
  1434  		}
  1435  		v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(res))))
  1436  
  1437  	case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL,
  1438  		opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
  1439  		opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL,
  1440  		opcode.JMPLT, opcode.JMPLTL, opcode.JMPLE, opcode.JMPLEL:
  1441  		offset := getJumpOffset(ctx, parameter)
  1442  		cond := true
  1443  		switch op {
  1444  		case opcode.JMP, opcode.JMPL:
  1445  		case opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL:
  1446  			cond = v.estack.Pop().Bool() == (op == opcode.JMPIF || op == opcode.JMPIFL)
  1447  		default:
  1448  			b := v.estack.Pop().BigInt()
  1449  			a := v.estack.Pop().BigInt()
  1450  			cond = getJumpCondition(op, a, b)
  1451  		}
  1452  
  1453  		if cond {
  1454  			ctx.Jump(offset)
  1455  		}
  1456  
  1457  	case opcode.CALL, opcode.CALLL:
  1458  		// Note: jump offset must be calculated regarding the new context,
  1459  		// but it is cloned and thus has the same script and instruction pointer.
  1460  		v.call(ctx, getJumpOffset(ctx, parameter))
  1461  
  1462  	case opcode.CALLA:
  1463  		ptr := v.estack.Pop().Item().(*stackitem.Pointer)
  1464  		if ptr.ScriptHash() != ctx.ScriptHash() {
  1465  			panic("invalid script in pointer")
  1466  		}
  1467  
  1468  		v.call(ctx, ptr.Position())
  1469  
  1470  	case opcode.CALLT:
  1471  		id := int32(binary.LittleEndian.Uint16(parameter))
  1472  		if err := v.LoadToken(id); err != nil {
  1473  			panic(err)
  1474  		}
  1475  
  1476  	case opcode.SYSCALL:
  1477  		interopID := GetInteropID(parameter)
  1478  		if v.SyscallHandler == nil {
  1479  			panic("vm's SyscallHandler is not initialized")
  1480  		}
  1481  		err := v.SyscallHandler(v, interopID)
  1482  		if err != nil {
  1483  			iName, iErr := interopnames.FromID(interopID)
  1484  			if iErr == nil {
  1485  				panic(fmt.Sprintf("%s failed: %s", iName, err))
  1486  			}
  1487  			panic(fmt.Sprintf("%d failed: %s", interopID, err))
  1488  		}
  1489  
  1490  	case opcode.RET:
  1491  		oldCtx := v.istack[len(v.istack)-1]
  1492  		v.istack = v.istack[:len(v.istack)-1]
  1493  		oldEstack := v.estack
  1494  
  1495  		v.unloadContext(oldCtx)
  1496  		if len(v.istack) == 0 {
  1497  			v.state = vmstate.Halt
  1498  			break
  1499  		}
  1500  
  1501  		newEstack := v.Context().sc.estack
  1502  		if oldEstack != newEstack {
  1503  			if oldCtx.retCount >= 0 && oldEstack.Len() != oldCtx.retCount {
  1504  				panic(fmt.Errorf("invalid return values count: expected %d, got %d",
  1505  					oldCtx.retCount, oldEstack.Len()))
  1506  			}
  1507  			rvcount := oldEstack.Len()
  1508  			for i := rvcount; i > 0; i-- {
  1509  				elem := oldEstack.RemoveAt(i - 1)
  1510  				newEstack.Push(elem)
  1511  			}
  1512  			v.estack = newEstack
  1513  		}
  1514  
  1515  	case opcode.NEWMAP:
  1516  		v.estack.PushItem(stackitem.NewMap())
  1517  
  1518  	case opcode.KEYS:
  1519  		if v.estack.Len() == 0 {
  1520  			panic("no argument")
  1521  		}
  1522  		item := v.estack.Pop()
  1523  
  1524  		m, ok := item.value.(*stackitem.Map)
  1525  		if !ok {
  1526  			panic("not a Map")
  1527  		}
  1528  
  1529  		arr := make([]stackitem.Item, 0, m.Len())
  1530  		for k := range m.Value().([]stackitem.MapElement) {
  1531  			arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup())
  1532  		}
  1533  		v.estack.PushItem(stackitem.NewArray(arr))
  1534  
  1535  	case opcode.VALUES:
  1536  		if v.estack.Len() == 0 {
  1537  			panic("no argument")
  1538  		}
  1539  		item := v.estack.Pop()
  1540  
  1541  		var arr []stackitem.Item
  1542  		switch t := item.value.(type) {
  1543  		case *stackitem.Array, *stackitem.Struct:
  1544  			src := t.Value().([]stackitem.Item)
  1545  			arr = make([]stackitem.Item, len(src))
  1546  			for i := range src {
  1547  				arr[i] = cloneIfStruct(src[i])
  1548  			}
  1549  		case *stackitem.Map:
  1550  			arr = make([]stackitem.Item, 0, t.Len())
  1551  			for k := range t.Value().([]stackitem.MapElement) {
  1552  				arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
  1553  			}
  1554  		default:
  1555  			panic("not a Map, Array or Struct")
  1556  		}
  1557  
  1558  		v.estack.PushItem(stackitem.NewArray(arr))
  1559  
  1560  	case opcode.HASKEY:
  1561  		if v.estack.Len() < 2 {
  1562  			panic("not enough arguments")
  1563  		}
  1564  		key := v.estack.Pop()
  1565  		validateMapKey(key)
  1566  
  1567  		c := v.estack.Pop()
  1568  		var res bool
  1569  		switch t := c.value.(type) {
  1570  		case *stackitem.Array, *stackitem.Struct:
  1571  			index := toInt(key.BigInt())
  1572  			if index < 0 {
  1573  				panic("negative index")
  1574  			}
  1575  			res = index < len(c.Array())
  1576  		case *stackitem.Map:
  1577  			res = t.Has(key.Item())
  1578  		case *stackitem.Buffer, *stackitem.ByteArray:
  1579  			index := toInt(key.BigInt())
  1580  			if index < 0 {
  1581  				panic("negative index")
  1582  			}
  1583  			res = index < len(t.Value().([]byte))
  1584  		default:
  1585  			panic("wrong collection type")
  1586  		}
  1587  		v.estack.PushItem(stackitem.Bool(res))
  1588  
  1589  	case opcode.NOP:
  1590  		// unlucky ^^
  1591  
  1592  	case opcode.THROW:
  1593  		v.throw(v.estack.Pop().Item())
  1594  
  1595  	case opcode.ABORT:
  1596  		panic("ABORT")
  1597  
  1598  	case opcode.ABORTMSG:
  1599  		msg := v.estack.Pop().Bytes()
  1600  		panic(fmt.Sprintf("%s is executed. Reason: %s", op, string(msg)))
  1601  
  1602  	case opcode.ASSERT:
  1603  		if !v.estack.Pop().Bool() {
  1604  			panic("ASSERT failed")
  1605  		}
  1606  
  1607  	case opcode.ASSERTMSG:
  1608  		msg := v.estack.Pop().Bytes()
  1609  		if !v.estack.Pop().Bool() {
  1610  			panic(fmt.Sprintf("%s is executed with false result. Reason: %s", op, msg))
  1611  		}
  1612  
  1613  	case opcode.TRY, opcode.TRYL:
  1614  		catchP, finallyP := getTryParams(op, parameter)
  1615  		if ctx.tryStack.Len() >= MaxTryNestingDepth {
  1616  			panic("maximum TRY depth exceeded")
  1617  		}
  1618  		cOffset := getJumpOffset(ctx, catchP)
  1619  		fOffset := getJumpOffset(ctx, finallyP)
  1620  		if cOffset == ctx.ip && fOffset == ctx.ip {
  1621  			panic("invalid offset for TRY*")
  1622  		} else if cOffset == ctx.ip {
  1623  			cOffset = -1
  1624  		} else if fOffset == ctx.ip {
  1625  			fOffset = -1
  1626  		}
  1627  		eCtx := newExceptionHandlingContext(cOffset, fOffset)
  1628  		ctx.tryStack.PushItem(eCtx)
  1629  
  1630  	case opcode.ENDTRY, opcode.ENDTRYL:
  1631  		eCtx := ctx.tryStack.Peek(0).Value().(*exceptionHandlingContext)
  1632  		if eCtx.State == eFinally {
  1633  			panic("invalid exception handling state during ENDTRY*")
  1634  		}
  1635  		eOffset := getJumpOffset(ctx, parameter)
  1636  		if eCtx.HasFinally() {
  1637  			eCtx.State = eFinally
  1638  			eCtx.EndOffset = eOffset
  1639  			eOffset = eCtx.FinallyOffset
  1640  		} else {
  1641  			ctx.tryStack.Pop()
  1642  		}
  1643  		ctx.Jump(eOffset)
  1644  
  1645  	case opcode.ENDFINALLY:
  1646  		if v.uncaughtException != nil {
  1647  			v.handleException()
  1648  			return
  1649  		}
  1650  		eCtx := ctx.tryStack.Pop().Value().(*exceptionHandlingContext)
  1651  		ctx.Jump(eCtx.EndOffset)
  1652  
  1653  	default:
  1654  		panic(fmt.Sprintf("unknown opcode %s", op.String()))
  1655  	}
  1656  	return
  1657  }
  1658  
  1659  func (v *VM) unloadContext(ctx *Context) {
  1660  	if ctx.local != nil {
  1661  		ctx.local.ClearRefs(&v.refs)
  1662  	}
  1663  	if ctx.arguments != nil {
  1664  		ctx.arguments.ClearRefs(&v.refs)
  1665  	}
  1666  	currCtx := v.Context()
  1667  	if currCtx == nil || ctx.sc != currCtx.sc {
  1668  		if ctx.sc.static != nil {
  1669  			ctx.sc.static.ClearRefs(&v.refs)
  1670  		}
  1671  		if ctx.sc.onUnload != nil {
  1672  			err := ctx.sc.onUnload(v, ctx, v.uncaughtException == nil)
  1673  			if err != nil {
  1674  				errMessage := fmt.Sprintf("context unload callback failed: %s", err)
  1675  				if v.uncaughtException != nil {
  1676  					errMessage = fmt.Sprintf("%s, uncaught exception: %s", errMessage, v.uncaughtException)
  1677  				}
  1678  				panic(errors.New(errMessage))
  1679  			}
  1680  		}
  1681  	}
  1682  }
  1683  
  1684  // getTryParams splits TRY(L) instruction parameter into offsets for catch and finally blocks.
  1685  func getTryParams(op opcode.Opcode, p []byte) ([]byte, []byte) {
  1686  	i := 1
  1687  	if op == opcode.TRYL {
  1688  		i = 4
  1689  	}
  1690  	return p[:i], p[i:]
  1691  }
  1692  
  1693  // getJumpCondition performs opcode specific comparison of a and b.
  1694  func getJumpCondition(op opcode.Opcode, a, b *big.Int) bool {
  1695  	cmp := a.Cmp(b)
  1696  	switch op {
  1697  	case opcode.JMPEQ, opcode.JMPEQL:
  1698  		return cmp == 0
  1699  	case opcode.JMPNE, opcode.JMPNEL:
  1700  		return cmp != 0
  1701  	case opcode.JMPGT, opcode.JMPGTL:
  1702  		return cmp > 0
  1703  	case opcode.JMPGE, opcode.JMPGEL:
  1704  		return cmp >= 0
  1705  	case opcode.JMPLT, opcode.JMPLTL:
  1706  		return cmp < 0
  1707  	case opcode.JMPLE, opcode.JMPLEL:
  1708  		return cmp <= 0
  1709  	default:
  1710  		panic(fmt.Sprintf("invalid JMP* opcode: %s", op))
  1711  	}
  1712  }
  1713  
  1714  func (v *VM) throw(item stackitem.Item) {
  1715  	v.uncaughtException = item
  1716  	v.handleException()
  1717  }
  1718  
  1719  // Call calls a method by offset using the new execution context.
  1720  func (v *VM) Call(offset int) {
  1721  	v.call(v.Context(), offset)
  1722  }
  1723  
  1724  // call is an internal representation of Call, which does not
  1725  // affect the invocation counter and is only used by vm
  1726  // package.
  1727  func (v *VM) call(ctx *Context, offset int) {
  1728  	v.checkInvocationStackSize()
  1729  	newCtx := &Context{
  1730  		sc:       ctx.sc,
  1731  		retCount: -1,
  1732  		tryStack: ctx.tryStack,
  1733  	}
  1734  	// New context -> new exception handlers.
  1735  	newCtx.tryStack.elems = ctx.tryStack.elems[len(ctx.tryStack.elems):]
  1736  	v.istack = append(v.istack, newCtx)
  1737  	newCtx.Jump(offset)
  1738  }
  1739  
  1740  // getJumpOffset returns an instruction number in the current context
  1741  // to which JMP should be performed.
  1742  // parameter should have length either 1 or 4 and
  1743  // is interpreted as little-endian.
  1744  func getJumpOffset(ctx *Context, parameter []byte) int {
  1745  	offset, _, err := calcJumpOffset(ctx, parameter)
  1746  	if err != nil {
  1747  		panic(err)
  1748  	}
  1749  	return offset
  1750  }
  1751  
  1752  // calcJumpOffset returns an absolute and a relative offset of JMP/CALL/TRY instructions
  1753  // either in a short (1-byte) or a long (4-byte) form.
  1754  func calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
  1755  	var rOffset int32
  1756  	switch l := len(parameter); l {
  1757  	case 1:
  1758  		rOffset = int32(int8(parameter[0]))
  1759  	case 4:
  1760  		rOffset = int32(binary.LittleEndian.Uint32(parameter))
  1761  	default:
  1762  		_, curr := ctx.CurrInstr()
  1763  		return 0, 0, fmt.Errorf("invalid %s parameter length: %d", curr, l)
  1764  	}
  1765  	offset := ctx.ip + int(rOffset)
  1766  	if offset < 0 || offset > len(ctx.sc.prog) {
  1767  		return 0, 0, fmt.Errorf("invalid offset %d ip at %d", offset, ctx.ip)
  1768  	}
  1769  
  1770  	return offset, int(rOffset), nil
  1771  }
  1772  
  1773  func (v *VM) handleException() {
  1774  	for pop := 0; pop < len(v.istack); pop++ {
  1775  		ictx := v.istack[len(v.istack)-1-pop]
  1776  		for j := 0; j < ictx.tryStack.Len(); j++ {
  1777  			e := ictx.tryStack.Peek(j)
  1778  			ectx := e.Value().(*exceptionHandlingContext)
  1779  			if ectx.State == eFinally || (ectx.State == eCatch && !ectx.HasFinally()) {
  1780  				ictx.tryStack.Pop()
  1781  				j = -1
  1782  				continue
  1783  			}
  1784  			for i := 0; i < pop; i++ {
  1785  				ctx := v.istack[len(v.istack)-1]
  1786  				v.istack = v.istack[:len(v.istack)-1]
  1787  				v.unloadContext(ctx)
  1788  			}
  1789  			v.estack = ictx.sc.estack
  1790  			if ectx.State == eTry && ectx.HasCatch() {
  1791  				ectx.State = eCatch
  1792  				v.estack.PushItem(v.uncaughtException)
  1793  				v.uncaughtException = nil
  1794  				ictx.Jump(ectx.CatchOffset)
  1795  			} else {
  1796  				ectx.State = eFinally
  1797  				ictx.Jump(ectx.FinallyOffset)
  1798  			}
  1799  			return
  1800  		}
  1801  	}
  1802  	throwUnhandledException(v.uncaughtException)
  1803  }
  1804  
  1805  // throwUnhandledException gets an exception message from the provided stackitem and panics.
  1806  func throwUnhandledException(item stackitem.Item) {
  1807  	msg := "unhandled exception"
  1808  	switch item.Type() {
  1809  	case stackitem.ArrayT:
  1810  		if arr := item.Value().([]stackitem.Item); len(arr) > 0 {
  1811  			data, err := arr[0].TryBytes()
  1812  			if err == nil {
  1813  				msg = fmt.Sprintf("%s: %q", msg, string(data))
  1814  			}
  1815  		}
  1816  	default:
  1817  		data, err := item.TryBytes()
  1818  		if err == nil {
  1819  			msg = fmt.Sprintf("%s: %q", msg, string(data))
  1820  		}
  1821  	}
  1822  	panic(msg)
  1823  }
  1824  
  1825  // ContractHasTryBlock checks if the currently executing contract has a TRY
  1826  // block in one of its contexts.
  1827  func (v *VM) ContractHasTryBlock() bool {
  1828  	var topctx *Context // Currently executing context.
  1829  	for i := 0; i < len(v.istack); i++ {
  1830  		ictx := v.istack[len(v.istack)-1-i] // It's a stack, going backwards like handleException().
  1831  		if topctx == nil {
  1832  			topctx = ictx
  1833  		}
  1834  		if ictx.sc != topctx.sc {
  1835  			return false // Different contract -> no one cares.
  1836  		}
  1837  		for j := 0; j < ictx.tryStack.Len(); j++ {
  1838  			eCtx := ictx.tryStack.Peek(j).Value().(*exceptionHandlingContext)
  1839  			if eCtx.State == eTry {
  1840  				return true
  1841  			}
  1842  		}
  1843  	}
  1844  	return false
  1845  }
  1846  
  1847  // CheckMultisigPar checks if the sigs contains sufficient valid signatures.
  1848  func CheckMultisigPar(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sigs [][]byte) bool {
  1849  	if len(sigs) == 1 {
  1850  		return checkMultisig1(v, curve, h, pkeys, sigs[0])
  1851  	}
  1852  
  1853  	k1, k2 := 0, len(pkeys)-1
  1854  	s1, s2 := 0, len(sigs)-1
  1855  
  1856  	type task struct {
  1857  		pub    *keys.PublicKey
  1858  		signum int
  1859  	}
  1860  
  1861  	type verify struct {
  1862  		ok     bool
  1863  		signum int
  1864  	}
  1865  
  1866  	worker := func(ch <-chan task, result chan verify) {
  1867  		for {
  1868  			t, ok := <-ch
  1869  			if !ok {
  1870  				return
  1871  			}
  1872  
  1873  			result <- verify{
  1874  				signum: t.signum,
  1875  				ok:     t.pub.Verify(sigs[t.signum], h),
  1876  			}
  1877  		}
  1878  	}
  1879  
  1880  	const workerCount = 3
  1881  	tasks := make(chan task, 2)
  1882  	results := make(chan verify, len(sigs))
  1883  	for i := 0; i < workerCount; i++ {
  1884  		go worker(tasks, results)
  1885  	}
  1886  
  1887  	tasks <- task{pub: bytesToPublicKey(pkeys[k1], curve), signum: s1}
  1888  	tasks <- task{pub: bytesToPublicKey(pkeys[k2], curve), signum: s2}
  1889  
  1890  	sigok := true
  1891  	taskCount := 2
  1892  
  1893  loop:
  1894  	for r := range results {
  1895  		goingForward := true
  1896  
  1897  		taskCount--
  1898  		if r.signum == s2 {
  1899  			goingForward = false
  1900  		}
  1901  		if k1+1 == k2 {
  1902  			sigok = r.ok && s1+1 == s2
  1903  			if taskCount != 0 && sigok {
  1904  				continue
  1905  			}
  1906  			break loop
  1907  		} else if r.ok {
  1908  			if s1+1 == s2 {
  1909  				if taskCount != 0 && sigok {
  1910  					continue
  1911  				}
  1912  				break loop
  1913  			}
  1914  			if goingForward {
  1915  				s1++
  1916  			} else {
  1917  				s2--
  1918  			}
  1919  		}
  1920  
  1921  		var nextSig, nextKey int
  1922  		if goingForward {
  1923  			k1++
  1924  			nextSig = s1
  1925  			nextKey = k1
  1926  		} else {
  1927  			k2--
  1928  			nextSig = s2
  1929  			nextKey = k2
  1930  		}
  1931  		taskCount++
  1932  		tasks <- task{pub: bytesToPublicKey(pkeys[nextKey], curve), signum: nextSig}
  1933  	}
  1934  
  1935  	close(tasks)
  1936  
  1937  	return sigok
  1938  }
  1939  
  1940  func checkMultisig1(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sig []byte) bool {
  1941  	for i := range pkeys {
  1942  		pkey := bytesToPublicKey(pkeys[i], curve)
  1943  		if pkey.Verify(sig, h) {
  1944  			return true
  1945  		}
  1946  	}
  1947  
  1948  	return false
  1949  }
  1950  
  1951  func cloneIfStruct(item stackitem.Item) stackitem.Item {
  1952  	switch it := item.(type) {
  1953  	case *stackitem.Struct:
  1954  		ret, err := it.Clone()
  1955  		if err != nil {
  1956  			panic(err)
  1957  		}
  1958  		return ret
  1959  	default:
  1960  		return it
  1961  	}
  1962  }
  1963  
  1964  func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
  1965  	if !typ.IsValid() {
  1966  		panic(fmt.Sprintf("invalid stack item type: %d", typ))
  1967  	}
  1968  	items := make([]stackitem.Item, n)
  1969  	for i := range items {
  1970  		switch typ {
  1971  		case stackitem.BooleanT:
  1972  			items[i] = stackitem.NewBool(false)
  1973  		case stackitem.IntegerT:
  1974  			items[i] = stackitem.NewBigInteger(big.NewInt(0))
  1975  		case stackitem.ByteArrayT:
  1976  			items[i] = stackitem.NewByteArray([]byte{})
  1977  		default:
  1978  			items[i] = stackitem.Null{}
  1979  		}
  1980  	}
  1981  	return items
  1982  }
  1983  
  1984  func validateMapKey(key Element) {
  1985  	item := key.Item()
  1986  	if item == nil {
  1987  		panic("no key found")
  1988  	}
  1989  	if err := stackitem.IsValidMapKey(item); err != nil {
  1990  		panic(err)
  1991  	}
  1992  }
  1993  
  1994  func (v *VM) checkInvocationStackSize() {
  1995  	if len(v.istack) >= MaxInvocationStackSize {
  1996  		panic("invocation stack is too big")
  1997  	}
  1998  }
  1999  
  2000  // bytesToPublicKey is a helper deserializing keys using cache and panicing on
  2001  // error.
  2002  func bytesToPublicKey(b []byte, curve elliptic.Curve) *keys.PublicKey {
  2003  	pkey, err := keys.NewPublicKeyFromBytes(b, curve)
  2004  	if err != nil {
  2005  		panic(err.Error())
  2006  	}
  2007  	return pkey
  2008  }
  2009  
  2010  // GetCallingScriptHash implements the ScriptHashGetter interface.
  2011  func (v *VM) GetCallingScriptHash() util.Uint160 {
  2012  	return v.Context().sc.callingScriptHash
  2013  }
  2014  
  2015  // GetEntryScriptHash implements the ScriptHashGetter interface.
  2016  func (v *VM) GetEntryScriptHash() util.Uint160 {
  2017  	return v.getContextScriptHash(len(v.istack) - 1)
  2018  }
  2019  
  2020  // GetCurrentScriptHash implements the ScriptHashGetter interface.
  2021  func (v *VM) GetCurrentScriptHash() util.Uint160 {
  2022  	return v.getContextScriptHash(0)
  2023  }
  2024  
  2025  // toInt converts an item to a 32-bit int.
  2026  func toInt(i *big.Int) int {
  2027  	if !i.IsInt64() {
  2028  		panic("not an int32")
  2029  	}
  2030  	n := i.Int64()
  2031  	if n < math.MinInt32 || n > math.MaxInt32 {
  2032  		panic("not an int32")
  2033  	}
  2034  	return int(n)
  2035  }