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 }