2018-03-30 16:15:06 +00:00
|
|
|
package vm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha1"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"math/big"
|
2018-04-02 15:04:42 +00:00
|
|
|
"os"
|
2019-08-20 16:46:52 +00:00
|
|
|
"reflect"
|
2019-09-09 08:23:27 +00:00
|
|
|
"text/tabwriter"
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-23 15:50:45 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
2019-09-23 16:54:06 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
2018-03-30 16:15:06 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/util"
|
|
|
|
)
|
|
|
|
|
2018-04-02 15:04:42 +00:00
|
|
|
// Mode configures behaviour of the VM.
|
|
|
|
type Mode uint
|
|
|
|
|
|
|
|
// Available VM Modes.
|
|
|
|
var (
|
|
|
|
ModeMute Mode = 1 << 0
|
|
|
|
)
|
|
|
|
|
2019-09-18 11:35:29 +00:00
|
|
|
const (
|
|
|
|
maxSHLArg = 256
|
|
|
|
minSHLArg = -256
|
|
|
|
)
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
// VM represents the virtual machine.
|
|
|
|
type VM struct {
|
|
|
|
state State
|
|
|
|
|
2018-04-10 09:45:31 +00:00
|
|
|
// registered interop hooks.
|
|
|
|
interop map[string]InteropFunc
|
2018-03-30 16:15:06 +00:00
|
|
|
|
|
|
|
// scripts loaded in memory.
|
|
|
|
scripts map[util.Uint160][]byte
|
|
|
|
|
|
|
|
istack *Stack // invocation stack.
|
|
|
|
estack *Stack // execution stack.
|
|
|
|
astack *Stack // alt stack.
|
|
|
|
|
|
|
|
// Mute all output after execution.
|
|
|
|
mute bool
|
2019-09-23 16:54:06 +00:00
|
|
|
// Hash to verify in CHECKSIG/CHECKMULTISIG.
|
|
|
|
checkhash []byte
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new VM object ready to load .avm bytecode scripts.
|
2018-04-10 09:45:31 +00:00
|
|
|
func New(mode Mode) *VM {
|
2018-04-02 15:04:42 +00:00
|
|
|
vm := &VM{
|
2018-04-10 09:45:31 +00:00
|
|
|
interop: make(map[string]InteropFunc),
|
2018-03-30 16:15:06 +00:00
|
|
|
scripts: make(map[util.Uint160][]byte),
|
|
|
|
state: haltState,
|
|
|
|
istack: NewStack("invocation"),
|
|
|
|
estack: NewStack("evaluation"),
|
|
|
|
astack: NewStack("alt"),
|
|
|
|
}
|
2018-04-02 15:04:42 +00:00
|
|
|
if mode == ModeMute {
|
|
|
|
vm.mute = true
|
|
|
|
}
|
2018-04-10 09:45:31 +00:00
|
|
|
|
|
|
|
// Register native interop hooks.
|
|
|
|
vm.RegisterInteropFunc("Neo.Runtime.Log", runtimeLog)
|
|
|
|
vm.RegisterInteropFunc("Neo.Runtime.Notify", runtimeNotify)
|
|
|
|
|
2018-04-02 15:04:42 +00:00
|
|
|
return vm
|
|
|
|
}
|
|
|
|
|
2018-04-10 09:45:31 +00:00
|
|
|
// RegisterInteropFunc will register the given InteropFunc to the VM.
|
|
|
|
func (v *VM) RegisterInteropFunc(name string, f InteropFunc) {
|
|
|
|
v.interop[name] = f
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// Estack will return the evaluation stack so interop hooks can utilize this.
|
2018-04-10 09:45:31 +00:00
|
|
|
func (v *VM) Estack() *Stack {
|
|
|
|
return v.estack
|
|
|
|
}
|
|
|
|
|
|
|
|
// Astack will return the alt stack so interop hooks can utilize this.
|
|
|
|
func (v *VM) Astack() *Stack {
|
|
|
|
return v.astack
|
|
|
|
}
|
|
|
|
|
|
|
|
// Istack will return the invocation stack so interop hooks can utilize this.
|
|
|
|
func (v *VM) Istack() *Stack {
|
|
|
|
return v.istack
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadArgs will load in the arguments used in the Mian entry point.
|
|
|
|
func (v *VM) LoadArgs(method []byte, args []StackItem) {
|
|
|
|
if len(args) > 0 {
|
|
|
|
v.estack.PushVal(args)
|
|
|
|
}
|
|
|
|
if method != nil {
|
2019-02-09 15:53:58 +00:00
|
|
|
v.estack.PushVal(method)
|
2018-04-10 09:45:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-02 15:04:42 +00:00
|
|
|
// PrintOps will print the opcodes of the current loaded program to stdout.
|
|
|
|
func (v *VM) PrintOps() {
|
|
|
|
prog := v.Context().Program()
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
|
|
|
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
|
|
|
cursor := ""
|
|
|
|
ip, _ := v.Context().CurrInstr()
|
|
|
|
for i := 0; i < len(prog); i++ {
|
|
|
|
if i == ip {
|
|
|
|
cursor = "<<"
|
|
|
|
} else {
|
|
|
|
cursor = ""
|
|
|
|
}
|
2019-08-14 12:40:31 +00:00
|
|
|
fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Instruction(prog[i]).String(), cursor)
|
2018-04-02 15:04:42 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
w.Flush()
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddBreakPoint adds a breakpoint to the current context.
|
|
|
|
func (v *VM) AddBreakPoint(n int) {
|
|
|
|
ctx := v.Context()
|
|
|
|
ctx.breakPoints = append(ctx.breakPoints, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddBreakPointRel adds a breakpoint relative to the current
|
|
|
|
// instruction pointer.
|
|
|
|
func (v *VM) AddBreakPointRel(n int) {
|
|
|
|
ctx := v.Context()
|
|
|
|
v.AddBreakPoint(ctx.ip + n)
|
|
|
|
}
|
|
|
|
|
2018-04-02 15:04:42 +00:00
|
|
|
// LoadFile will load a program from the given path, ready to execute it.
|
|
|
|
func (v *VM) LoadFile(path string) error {
|
2018-03-30 16:15:06 +00:00
|
|
|
b, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-02 15:04:42 +00:00
|
|
|
v.Load(b)
|
2018-03-30 16:15:06 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-09-03 14:51:37 +00:00
|
|
|
// Load initializes the VM with the program given.
|
2018-04-02 15:04:42 +00:00
|
|
|
func (v *VM) Load(prog []byte) {
|
|
|
|
// clear all stacks, it could be a reload.
|
|
|
|
v.istack.Clear()
|
|
|
|
v.estack.Clear()
|
|
|
|
v.astack.Clear()
|
|
|
|
v.istack.PushVal(NewContext(prog))
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
// LoadScript will load a script from the internal script table. It
|
2019-02-09 15:53:58 +00:00
|
|
|
// will immediately push a new context created from this script to
|
2018-03-30 16:15:06 +00:00
|
|
|
// the invocation stack and starts executing it.
|
|
|
|
func (v *VM) LoadScript(b []byte) {
|
|
|
|
ctx := NewContext(b)
|
|
|
|
v.istack.PushVal(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Context returns the current executed context. Nil if there is no context,
|
|
|
|
// which implies no program is loaded.
|
|
|
|
func (v *VM) Context() *Context {
|
|
|
|
if v.istack.Len() == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return v.istack.Peek(0).value.Value().(*Context)
|
|
|
|
}
|
|
|
|
|
2018-04-02 15:04:42 +00:00
|
|
|
// PopResult is used to pop the first item of the evaluation stack. This allows
|
|
|
|
// us to test compiler and vm in a bi-directional way.
|
|
|
|
func (v *VM) PopResult() interface{} {
|
|
|
|
return v.estack.Pop().value.Value()
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
// Stack returns json formatted representation of the given stack.
|
|
|
|
func (v *VM) Stack(n string) string {
|
|
|
|
var s *Stack
|
|
|
|
if n == "astack" {
|
|
|
|
s = v.astack
|
|
|
|
}
|
|
|
|
if n == "istack" {
|
|
|
|
s = v.istack
|
|
|
|
}
|
|
|
|
if n == "estack" {
|
|
|
|
s = v.estack
|
|
|
|
}
|
|
|
|
return buildStackOutput(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ready return true if the VM ready to execute the loaded program.
|
|
|
|
// Will return false if no program is loaded.
|
|
|
|
func (v *VM) Ready() bool {
|
|
|
|
return v.istack.Len() > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run starts the execution of the loaded program.
|
|
|
|
func (v *VM) Run() {
|
|
|
|
if !v.Ready() {
|
|
|
|
fmt.Println("no program loaded")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
v.state = noneState
|
|
|
|
for {
|
2019-02-19 11:47:25 +00:00
|
|
|
switch {
|
|
|
|
case v.state.HasFlag(haltState):
|
2018-03-30 16:15:06 +00:00
|
|
|
if !v.mute {
|
|
|
|
fmt.Println(v.Stack("estack"))
|
|
|
|
}
|
|
|
|
return
|
2019-02-19 11:47:25 +00:00
|
|
|
case v.state.HasFlag(breakState):
|
2018-03-30 16:15:06 +00:00
|
|
|
ctx := v.Context()
|
|
|
|
i, op := ctx.CurrInstr()
|
2019-02-09 15:53:58 +00:00
|
|
|
fmt.Printf("at breakpoint %d (%s)\n", i, op.String())
|
2018-03-30 16:15:06 +00:00
|
|
|
return
|
2019-02-19 11:47:25 +00:00
|
|
|
case v.state.HasFlag(faultState):
|
2018-03-30 16:15:06 +00:00
|
|
|
fmt.Println("FAULT")
|
|
|
|
return
|
2019-02-19 11:47:25 +00:00
|
|
|
case v.state == noneState:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.Step()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 1 instruction in the program.
|
|
|
|
func (v *VM) Step() {
|
|
|
|
ctx := v.Context()
|
|
|
|
op := ctx.Next()
|
|
|
|
v.execute(ctx, op)
|
|
|
|
|
|
|
|
// re-peek the context as it could been changed during execution.
|
|
|
|
cctx := v.Context()
|
|
|
|
if cctx != nil && cctx.atBreakPoint() {
|
|
|
|
v.state = breakState
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 15:46:47 +00:00
|
|
|
// HasFailed returns whether VM is in the failed state now. Usually used to
|
|
|
|
// check status after Run.
|
|
|
|
func (v *VM) HasFailed() bool {
|
|
|
|
return v.state.HasFlag(faultState)
|
|
|
|
}
|
|
|
|
|
2019-09-23 16:54:06 +00:00
|
|
|
// SetCheckedHash sets checked hash for CHECKSIG and CHECKMULTISIG instructions.
|
|
|
|
func (v *VM) SetCheckedHash(h []byte) {
|
|
|
|
v.checkhash = make([]byte, len(h))
|
|
|
|
copy(v.checkhash, h)
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
// execute performs an instruction cycle in the VM. Acting on the instruction (opcode).
|
2019-08-14 12:40:31 +00:00
|
|
|
func (v *VM) execute(ctx *Context, op Instruction) {
|
2019-02-09 15:53:58 +00:00
|
|
|
// Instead of polluting the whole VM logic with error handling, we will recover
|
2018-03-30 16:15:06 +00:00
|
|
|
// each panic at a central point, putting the VM in a fault state.
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
log.Printf("error encountered at instruction %d (%s)", ctx.ip, op)
|
|
|
|
log.Println(err)
|
|
|
|
v.state = faultState
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
if op >= PUSHBYTES1 && op <= PUSHBYTES75 {
|
2018-03-30 16:15:06 +00:00
|
|
|
b := ctx.readBytes(int(op))
|
2019-09-09 09:07:03 +00:00
|
|
|
if b == nil {
|
|
|
|
panic("failed to read instruction parameter")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch op {
|
2019-08-14 12:40:31 +00:00
|
|
|
case PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5,
|
|
|
|
PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11,
|
|
|
|
PUSH12, PUSH13, PUSH14, PUSH15, PUSH16:
|
|
|
|
val := int(op) - int(PUSH1) + 1
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(val)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PUSH0:
|
2019-09-12 08:42:27 +00:00
|
|
|
v.estack.PushVal([]byte{})
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PUSHDATA1:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := ctx.readByte()
|
|
|
|
b := ctx.readBytes(int(n))
|
2019-09-11 08:52:39 +00:00
|
|
|
if b == nil {
|
|
|
|
panic("failed to read instruction parameter")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(b)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PUSHDATA2:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := ctx.readUint16()
|
|
|
|
b := ctx.readBytes(int(n))
|
2019-09-11 08:52:39 +00:00
|
|
|
if b == nil {
|
|
|
|
panic("failed to read instruction parameter")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(b)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PUSHDATA4:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := ctx.readUint32()
|
|
|
|
b := ctx.readBytes(int(n))
|
2019-09-11 08:52:39 +00:00
|
|
|
if b == nil {
|
|
|
|
panic("failed to read instruction parameter")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(b)
|
|
|
|
|
|
|
|
// Stack operations.
|
2019-08-14 12:40:31 +00:00
|
|
|
case TOALTSTACK:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.astack.Push(v.estack.Pop())
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case FROMALTSTACK:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.Push(v.astack.Pop())
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DUPFROMALTSTACK:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.Push(v.astack.Dup(0))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DUP:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.Push(v.estack.Dup(0))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SWAP:
|
2018-03-30 16:15:06 +00:00
|
|
|
a := v.estack.Pop()
|
|
|
|
b := v.estack.Pop()
|
|
|
|
v.estack.Push(a)
|
|
|
|
v.estack.Push(b)
|
|
|
|
|
2019-09-05 13:34:35 +00:00
|
|
|
case TUCK:
|
|
|
|
a := v.estack.Dup(0)
|
|
|
|
if a == nil {
|
|
|
|
panic("no top-level element found")
|
|
|
|
}
|
|
|
|
if v.estack.Len() < 2 {
|
|
|
|
panic("can't TUCK with a one-element stack")
|
|
|
|
}
|
|
|
|
v.estack.InsertAt(a, 2)
|
2019-09-06 06:12:19 +00:00
|
|
|
case CAT:
|
|
|
|
b := v.estack.Pop().Bytes()
|
|
|
|
a := v.estack.Pop().Bytes()
|
|
|
|
ab := append(a, b...)
|
|
|
|
v.estack.PushVal(ab)
|
|
|
|
case SUBSTR:
|
|
|
|
l := int(v.estack.Pop().BigInt().Int64())
|
2019-09-11 09:00:11 +00:00
|
|
|
if l < 0 {
|
|
|
|
panic("negative length")
|
|
|
|
}
|
2019-09-18 11:19:58 +00:00
|
|
|
o := int(v.estack.Pop().BigInt().Int64())
|
2019-09-11 09:00:11 +00:00
|
|
|
if o < 0 {
|
|
|
|
panic("negative index")
|
|
|
|
}
|
2019-09-18 11:19:58 +00:00
|
|
|
s := v.estack.Pop().Bytes()
|
|
|
|
if o > len(s) {
|
|
|
|
panic("invalid offset")
|
|
|
|
}
|
|
|
|
last := l + o
|
|
|
|
if last > len(s) {
|
|
|
|
last = len(s)
|
2019-09-11 09:00:11 +00:00
|
|
|
}
|
2019-09-18 11:19:58 +00:00
|
|
|
v.estack.PushVal(s[o:last])
|
2019-09-06 06:12:19 +00:00
|
|
|
case LEFT:
|
|
|
|
l := int(v.estack.Pop().BigInt().Int64())
|
2019-09-11 09:03:43 +00:00
|
|
|
if l < 0 {
|
|
|
|
panic("negative length")
|
|
|
|
}
|
2019-09-06 06:12:19 +00:00
|
|
|
s := v.estack.Pop().Bytes()
|
2019-09-09 14:05:40 +00:00
|
|
|
if t := len(s); l > t {
|
|
|
|
l = t
|
|
|
|
}
|
2019-09-06 06:12:19 +00:00
|
|
|
v.estack.PushVal(s[:l])
|
|
|
|
case RIGHT:
|
|
|
|
l := int(v.estack.Pop().BigInt().Int64())
|
2019-09-11 09:03:43 +00:00
|
|
|
if l < 0 {
|
|
|
|
panic("negative length")
|
|
|
|
}
|
2019-09-06 06:12:19 +00:00
|
|
|
s := v.estack.Pop().Bytes()
|
|
|
|
v.estack.PushVal(s[len(s)-l:])
|
2019-09-05 13:42:54 +00:00
|
|
|
case XDROP:
|
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
|
|
|
if n < 0 {
|
|
|
|
panic("invalid length")
|
|
|
|
}
|
|
|
|
e := v.estack.RemoveAt(n)
|
|
|
|
if e == nil {
|
|
|
|
panic("bad index")
|
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case XSWAP:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
|
|
|
if n < 0 {
|
|
|
|
panic("XSWAP: invalid length")
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// Swap values of elements instead of reordering stack elements.
|
2018-03-30 16:15:06 +00:00
|
|
|
if n > 0 {
|
|
|
|
a := v.estack.Peek(n)
|
|
|
|
b := v.estack.Peek(0)
|
|
|
|
aval := a.value
|
|
|
|
bval := b.value
|
|
|
|
a.value = bval
|
|
|
|
b.value = aval
|
|
|
|
}
|
|
|
|
|
2019-09-05 12:43:59 +00:00
|
|
|
case XTUCK:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
2019-09-12 07:39:54 +00:00
|
|
|
if n <= 0 {
|
2019-09-05 12:43:59 +00:00
|
|
|
panic("XTUCK: invalid length")
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
2019-09-05 12:43:59 +00:00
|
|
|
a := v.estack.Dup(0)
|
|
|
|
if a == nil {
|
|
|
|
panic("no top-level element found")
|
|
|
|
}
|
|
|
|
if n > v.estack.Len() {
|
|
|
|
panic("can't push to the position specified")
|
|
|
|
}
|
|
|
|
v.estack.InsertAt(a, n)
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case ROT:
|
2019-09-12 07:57:55 +00:00
|
|
|
e := v.estack.RemoveAt(2)
|
|
|
|
if e == nil {
|
|
|
|
panic("no top-level element found")
|
|
|
|
}
|
|
|
|
v.estack.Push(e)
|
2018-04-02 15:04:42 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DEPTH:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.PushVal(v.estack.Len())
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NIP:
|
2019-09-12 08:09:23 +00:00
|
|
|
elem := v.estack.RemoveAt(1)
|
|
|
|
if elem == nil {
|
|
|
|
panic("no second element found")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case OVER:
|
2019-09-18 11:24:42 +00:00
|
|
|
a := v.estack.Peek(1)
|
2019-09-05 12:57:44 +00:00
|
|
|
if a == nil {
|
|
|
|
panic("no second element found")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.Push(a)
|
|
|
|
|
2019-09-05 12:18:04 +00:00
|
|
|
case PICK:
|
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
|
|
|
if n < 0 {
|
|
|
|
panic("negative stack item returned")
|
|
|
|
}
|
|
|
|
a := v.estack.Peek(n)
|
2019-09-05 12:57:44 +00:00
|
|
|
if a == nil {
|
|
|
|
panic("no nth element found")
|
|
|
|
}
|
2019-09-05 12:18:04 +00:00
|
|
|
v.estack.Push(a)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case ROLL:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
|
|
|
if n < 0 {
|
|
|
|
panic("negative stack item returned")
|
|
|
|
}
|
|
|
|
if n > 0 {
|
2019-08-31 06:04:59 +00:00
|
|
|
e := v.estack.RemoveAt(n)
|
|
|
|
if e == nil {
|
|
|
|
panic("bad index")
|
|
|
|
}
|
|
|
|
v.estack.Push(e)
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DROP:
|
2019-09-12 08:05:10 +00:00
|
|
|
if v.estack.Len() < 1 {
|
|
|
|
panic("stack is too small")
|
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
v.estack.Pop()
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case EQUAL:
|
2019-08-20 16:46:52 +00:00
|
|
|
b := v.estack.Pop()
|
2019-09-12 07:50:43 +00:00
|
|
|
if b == nil {
|
|
|
|
panic("no top-level element found")
|
|
|
|
}
|
2019-08-20 16:46:52 +00:00
|
|
|
a := v.estack.Pop()
|
2019-09-12 07:50:43 +00:00
|
|
|
if a == nil {
|
|
|
|
panic("no second-to-the-top element found")
|
|
|
|
}
|
2019-09-18 11:10:10 +00:00
|
|
|
if ta, ok := a.value.(*ArrayItem); ok {
|
|
|
|
if tb, ok := b.value.(*ArrayItem); ok {
|
|
|
|
v.estack.PushVal(ta == tb)
|
|
|
|
break
|
|
|
|
}
|
2019-09-24 13:45:41 +00:00
|
|
|
} else if ma, ok := a.value.(*MapItem); ok {
|
|
|
|
if mb, ok := b.value.(*MapItem); ok {
|
|
|
|
v.estack.PushVal(ma == mb)
|
|
|
|
break
|
|
|
|
}
|
2019-09-18 11:10:10 +00:00
|
|
|
}
|
2019-09-09 08:23:27 +00:00
|
|
|
v.estack.PushVal(reflect.DeepEqual(a, b))
|
2018-03-30 16:15:06 +00:00
|
|
|
|
|
|
|
// Bit operations.
|
2019-09-05 14:20:53 +00:00
|
|
|
case INVERT:
|
|
|
|
// inplace
|
|
|
|
a := v.estack.Peek(0).BigInt()
|
|
|
|
a.Not(a)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case AND:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).And(b, a))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case OR:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Or(b, a))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case XOR:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Xor(b, a))
|
|
|
|
|
|
|
|
// Numeric operations.
|
2019-08-14 12:40:31 +00:00
|
|
|
case ADD:
|
2018-03-30 16:15:06 +00:00
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Add(a, b))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SUB:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Sub(a, b))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DIV:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Div(a, b))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case MUL:
|
2018-03-30 16:15:06 +00:00
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Mul(a, b))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case MOD:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Mod(a, b))
|
|
|
|
|
2019-09-18 11:35:29 +00:00
|
|
|
case SHL, SHR:
|
|
|
|
b := v.estack.Pop().BigInt().Int64()
|
|
|
|
if b == 0 {
|
2019-09-12 09:02:38 +00:00
|
|
|
return
|
2019-09-18 11:35:29 +00:00
|
|
|
} else if b < minSHLArg || b > maxSHLArg {
|
|
|
|
panic(fmt.Sprintf("operand must be between %d and %d", minSHLArg, maxSHLArg))
|
2019-09-12 09:02:38 +00:00
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
a := v.estack.Pop().BigInt()
|
2019-09-18 11:35:29 +00:00
|
|
|
if op == SHL {
|
|
|
|
v.estack.PushVal(new(big.Int).Lsh(a, uint(b)))
|
|
|
|
} else {
|
|
|
|
v.estack.PushVal(new(big.Int).Rsh(a, uint(b)))
|
2019-09-12 09:02:38 +00:00
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case BOOLAND:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bool()
|
|
|
|
a := v.estack.Pop().Bool()
|
|
|
|
v.estack.PushVal(a && b)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case BOOLOR:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bool()
|
|
|
|
a := v.estack.Pop().Bool()
|
|
|
|
v.estack.PushVal(a || b)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NUMEQUAL:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) == 0)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NUMNOTEQUAL:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) != 0)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case LT:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) == -1)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case GT:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) == 1)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case LTE:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) <= 0)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case GTE:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(b) >= 0)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case MIN:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
val := a
|
|
|
|
if a.Cmp(b) == 1 {
|
|
|
|
val = b
|
|
|
|
}
|
|
|
|
v.estack.PushVal(val)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case MAX:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
val := a
|
|
|
|
if a.Cmp(b) == -1 {
|
|
|
|
val = b
|
|
|
|
}
|
|
|
|
v.estack.PushVal(val)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case WITHIN:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().BigInt()
|
|
|
|
a := v.estack.Pop().BigInt()
|
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case INC:
|
2018-03-30 16:15:06 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Add(x, big.NewInt(1)))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case DEC:
|
2018-03-30 16:15:06 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(new(big.Int).Sub(x, big.NewInt(1)))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SIGN:
|
2018-03-30 16:15:06 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(x.Sign())
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NEGATE:
|
2018-03-30 16:15:06 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(x.Neg(x))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case ABS:
|
2018-03-30 16:15:06 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(x.Abs(x))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NOT:
|
2018-04-04 19:41:19 +00:00
|
|
|
x := v.estack.Pop().Bool()
|
|
|
|
v.estack.PushVal(!x)
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NZ:
|
2019-09-05 12:05:44 +00:00
|
|
|
x := v.estack.Pop().BigInt()
|
|
|
|
v.estack.PushVal(x.Cmp(big.NewInt(0)) != 0)
|
2018-03-30 16:15:06 +00:00
|
|
|
|
|
|
|
// Object operations.
|
2019-08-14 12:40:31 +00:00
|
|
|
case NEWARRAY:
|
2019-09-06 16:00:04 +00:00
|
|
|
item := v.estack.Pop()
|
|
|
|
switch t := item.value.(type) {
|
|
|
|
case *StructItem:
|
|
|
|
v.estack.PushVal(&ArrayItem{t.value})
|
|
|
|
case *ArrayItem:
|
|
|
|
v.estack.PushVal(t)
|
|
|
|
default:
|
2019-09-12 08:48:39 +00:00
|
|
|
n := item.BigInt()
|
|
|
|
items := makeArrayOfFalses(int(n.Int64()))
|
|
|
|
v.estack.PushVal(&ArrayItem{items})
|
2019-09-06 16:00:04 +00:00
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NEWSTRUCT:
|
2019-09-06 16:00:04 +00:00
|
|
|
item := v.estack.Pop()
|
|
|
|
switch t := item.value.(type) {
|
|
|
|
case *ArrayItem:
|
|
|
|
v.estack.PushVal(&StructItem{t.value})
|
|
|
|
case *StructItem:
|
|
|
|
v.estack.PushVal(t)
|
|
|
|
default:
|
2019-09-12 08:48:39 +00:00
|
|
|
n := item.BigInt()
|
|
|
|
items := makeArrayOfFalses(int(n.Int64()))
|
|
|
|
v.estack.PushVal(&StructItem{items})
|
2019-09-06 16:00:04 +00:00
|
|
|
}
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case APPEND:
|
2018-03-30 16:15:06 +00:00
|
|
|
itemElem := v.estack.Pop()
|
|
|
|
arrElem := v.estack.Pop()
|
|
|
|
|
2019-09-25 09:18:37 +00:00
|
|
|
val := cloneIfStruct(itemElem.value)
|
2019-09-18 11:03:15 +00:00
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
switch t := arrElem.value.(type) {
|
2019-09-11 14:05:56 +00:00
|
|
|
case *ArrayItem:
|
|
|
|
arr := t.Value().([]StackItem)
|
2019-09-18 11:03:15 +00:00
|
|
|
arr = append(arr, val)
|
2019-09-11 14:05:56 +00:00
|
|
|
t.value = arr
|
|
|
|
case *StructItem:
|
2018-03-30 16:15:06 +00:00
|
|
|
arr := t.Value().([]StackItem)
|
2019-09-18 11:03:15 +00:00
|
|
|
arr = append(arr, val)
|
2019-09-11 14:05:56 +00:00
|
|
|
t.value = arr
|
2018-03-30 16:15:06 +00:00
|
|
|
default:
|
|
|
|
panic("APPEND: not of underlying type Array")
|
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PACK:
|
2018-03-30 16:15:06 +00:00
|
|
|
n := int(v.estack.Pop().BigInt().Int64())
|
|
|
|
if n < 0 || n > v.estack.Len() {
|
|
|
|
panic("OPACK: invalid length")
|
|
|
|
}
|
|
|
|
|
|
|
|
items := make([]StackItem, n)
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
items[i] = v.estack.Pop().value
|
|
|
|
}
|
|
|
|
|
|
|
|
v.estack.PushVal(items)
|
|
|
|
|
2019-09-06 08:32:20 +00:00
|
|
|
case UNPACK:
|
|
|
|
a := v.estack.Pop().Array()
|
|
|
|
l := len(a)
|
|
|
|
for i := l - 1; i >= 0; i-- {
|
|
|
|
v.estack.PushVal(a[i])
|
|
|
|
}
|
|
|
|
v.estack.PushVal(l)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case PICKITEM:
|
2019-09-24 12:58:31 +00:00
|
|
|
key := v.estack.Pop()
|
|
|
|
validateMapKey(key)
|
|
|
|
|
|
|
|
obj := v.estack.Pop()
|
|
|
|
index := int(key.BigInt().Int64())
|
2018-03-30 16:15:06 +00:00
|
|
|
|
|
|
|
switch t := obj.value.(type) {
|
|
|
|
// Struct and Array items have their underlying value as []StackItem.
|
2018-04-02 15:04:42 +00:00
|
|
|
case *ArrayItem, *StructItem:
|
2018-03-30 16:15:06 +00:00
|
|
|
arr := t.Value().([]StackItem)
|
|
|
|
if index < 0 || index >= len(arr) {
|
|
|
|
panic("PICKITEM: invalid index")
|
|
|
|
}
|
|
|
|
item := arr[index]
|
|
|
|
v.estack.PushVal(item)
|
2019-09-24 12:58:31 +00:00
|
|
|
case *MapItem:
|
|
|
|
if !t.Has(key.value) {
|
|
|
|
panic("invalid key")
|
|
|
|
}
|
|
|
|
k := toMapKey(key.value)
|
|
|
|
v.estack.Push(&Element{value: t.value[k]})
|
2018-03-30 16:15:06 +00:00
|
|
|
default:
|
2019-09-12 08:19:25 +00:00
|
|
|
arr := obj.Bytes()
|
|
|
|
if index < 0 || index >= len(arr) {
|
|
|
|
panic("PICKITEM: invalid index")
|
|
|
|
}
|
|
|
|
item := arr[index]
|
|
|
|
v.estack.PushVal(int(item))
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SETITEM:
|
2019-09-24 12:58:31 +00:00
|
|
|
item := v.estack.Pop().value
|
|
|
|
key := v.estack.Pop()
|
|
|
|
validateMapKey(key)
|
|
|
|
|
|
|
|
obj := v.estack.Pop()
|
2018-03-30 16:15:06 +00:00
|
|
|
|
|
|
|
switch t := obj.value.(type) {
|
|
|
|
// Struct and Array items have their underlying value as []StackItem.
|
2018-04-02 15:04:42 +00:00
|
|
|
case *ArrayItem, *StructItem:
|
2018-03-30 16:15:06 +00:00
|
|
|
arr := t.Value().([]StackItem)
|
2019-09-24 12:58:31 +00:00
|
|
|
index := int(key.BigInt().Int64())
|
2018-03-30 16:15:06 +00:00
|
|
|
if index < 0 || index >= len(arr) {
|
2018-04-02 15:04:42 +00:00
|
|
|
panic("SETITEM: invalid index")
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
arr[index] = item
|
2019-09-24 12:58:31 +00:00
|
|
|
case *MapItem:
|
|
|
|
t.Add(key.value, item)
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
default:
|
2018-04-02 15:04:42 +00:00
|
|
|
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
2019-09-06 08:32:20 +00:00
|
|
|
case REVERSE:
|
2019-09-16 12:46:35 +00:00
|
|
|
a := v.estack.Pop().Array()
|
2019-09-06 08:32:20 +00:00
|
|
|
if len(a) > 1 {
|
|
|
|
for i, j := 0, len(a)-1; i <= j; i, j = i+1, j-1 {
|
|
|
|
a[i], a[j] = a[j], a[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case REMOVE:
|
2019-09-24 13:16:24 +00:00
|
|
|
key := v.estack.Pop()
|
|
|
|
validateMapKey(key)
|
|
|
|
|
2019-09-12 08:32:09 +00:00
|
|
|
elem := v.estack.Pop()
|
|
|
|
switch t := elem.value.(type) {
|
|
|
|
case *ArrayItem:
|
|
|
|
a := t.value
|
2019-09-24 13:16:24 +00:00
|
|
|
k := int(key.BigInt().Int64())
|
|
|
|
if k < 0 || k >= len(a) {
|
2019-09-12 08:32:09 +00:00
|
|
|
panic("REMOVE: invalid index")
|
|
|
|
}
|
2019-09-24 13:16:24 +00:00
|
|
|
a = append(a[:k], a[k+1:]...)
|
2019-09-12 08:32:09 +00:00
|
|
|
t.value = a
|
|
|
|
case *StructItem:
|
|
|
|
a := t.value
|
2019-09-24 13:16:24 +00:00
|
|
|
k := int(key.BigInt().Int64())
|
|
|
|
if k < 0 || k >= len(a) {
|
2019-09-12 08:32:09 +00:00
|
|
|
panic("REMOVE: invalid index")
|
|
|
|
}
|
2019-09-24 13:16:24 +00:00
|
|
|
a = append(a[:k], a[k+1:]...)
|
2019-09-12 08:32:09 +00:00
|
|
|
t.value = a
|
2019-09-24 13:16:24 +00:00
|
|
|
case *MapItem:
|
|
|
|
m := t.value
|
|
|
|
k := toMapKey(key.value)
|
|
|
|
delete(m, k)
|
2019-09-12 08:32:09 +00:00
|
|
|
default:
|
|
|
|
panic("REMOVE: invalid type")
|
|
|
|
}
|
2019-09-06 08:32:20 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case ARRAYSIZE:
|
2018-03-30 16:15:06 +00:00
|
|
|
elem := v.estack.Pop()
|
2018-04-22 18:11:37 +00:00
|
|
|
// Cause there is no native (byte) item type here, hence we need to check
|
|
|
|
// the type of the item for array size operations.
|
|
|
|
switch t := elem.value.Value().(type) {
|
|
|
|
case []StackItem:
|
|
|
|
v.estack.PushVal(len(t))
|
2019-09-24 13:50:37 +00:00
|
|
|
case map[interface{}]StackItem:
|
2018-04-22 18:11:37 +00:00
|
|
|
v.estack.PushVal(len(t))
|
|
|
|
default:
|
2019-09-24 13:50:37 +00:00
|
|
|
v.estack.PushVal(len(elem.Bytes()))
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SIZE:
|
2018-04-04 19:41:19 +00:00
|
|
|
elem := v.estack.Pop()
|
2019-09-12 08:53:11 +00:00
|
|
|
arr := elem.Bytes()
|
2018-04-04 19:41:19 +00:00
|
|
|
v.estack.PushVal(len(arr))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case JMP, JMPIF, JMPIFNOT:
|
2018-04-02 15:04:42 +00:00
|
|
|
var (
|
|
|
|
rOffset = int16(ctx.readUint16())
|
|
|
|
offset = ctx.ip + int(rOffset) - 3 // sizeOf(int16 + uint8)
|
|
|
|
)
|
2018-03-30 16:15:06 +00:00
|
|
|
if offset < 0 || offset > len(ctx.prog) {
|
2018-04-02 15:04:42 +00:00
|
|
|
panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip))
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
cond := true
|
2019-08-14 12:40:31 +00:00
|
|
|
if op > JMP {
|
2018-03-30 16:15:06 +00:00
|
|
|
cond = v.estack.Pop().Bool()
|
2019-08-14 12:40:31 +00:00
|
|
|
if op == JMPIFNOT {
|
2018-03-30 16:15:06 +00:00
|
|
|
cond = !cond
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if cond {
|
|
|
|
ctx.ip = offset
|
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case CALL:
|
2018-03-30 16:15:06 +00:00
|
|
|
v.istack.PushVal(ctx.Copy())
|
|
|
|
ctx.ip += 2
|
2019-08-14 12:40:31 +00:00
|
|
|
v.execute(v.Context(), JMP)
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SYSCALL:
|
2018-03-30 16:15:06 +00:00
|
|
|
api := ctx.readVarBytes()
|
2018-04-10 09:45:31 +00:00
|
|
|
ifunc, ok := v.interop[string(api)]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("interop hook (%s) not registered", api))
|
|
|
|
}
|
|
|
|
if err := ifunc(v); err != nil {
|
2018-03-30 16:15:06 +00:00
|
|
|
panic(fmt.Sprintf("failed to invoke syscall: %s", err))
|
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case APPCALL, TAILCALL:
|
2018-03-30 16:15:06 +00:00
|
|
|
if len(v.scripts) == 0 {
|
|
|
|
panic("script table is empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := util.Uint160DecodeBytes(ctx.readBytes(20))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
script, ok := v.scripts[hash]
|
|
|
|
if !ok {
|
|
|
|
panic("could not find script")
|
|
|
|
}
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
if op == TAILCALL {
|
2018-03-30 16:15:06 +00:00
|
|
|
_ = v.istack.Pop()
|
|
|
|
}
|
|
|
|
|
|
|
|
v.LoadScript(script)
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case RET:
|
2018-03-30 16:15:06 +00:00
|
|
|
_ = v.istack.Pop()
|
|
|
|
if v.istack.Len() == 0 {
|
|
|
|
v.state = haltState
|
|
|
|
}
|
|
|
|
|
2019-09-23 16:54:06 +00:00
|
|
|
case CHECKSIG, VERIFY:
|
|
|
|
var hashToCheck []byte
|
|
|
|
|
|
|
|
keyb := v.estack.Pop().Bytes()
|
|
|
|
signature := v.estack.Pop().Bytes()
|
|
|
|
if op == CHECKSIG {
|
|
|
|
if v.checkhash == nil {
|
|
|
|
panic("VM is not set up properly for signature checks")
|
|
|
|
}
|
|
|
|
hashToCheck = v.checkhash
|
|
|
|
} else { // VERIFY
|
|
|
|
msg := v.estack.Pop().Bytes()
|
|
|
|
hashToCheck = hash.Sha256(msg).Bytes()
|
|
|
|
}
|
|
|
|
pkey := &keys.PublicKey{}
|
|
|
|
err := pkey.DecodeBytes(keyb)
|
|
|
|
if err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
|
|
|
res := pkey.Verify(signature, hashToCheck)
|
|
|
|
v.estack.PushVal(res)
|
|
|
|
|
|
|
|
case CHECKMULTISIG:
|
|
|
|
pkeys, err := v.estack.popSigElements()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("wrong parameters: %s", err.Error()))
|
|
|
|
}
|
|
|
|
sigs, err := v.estack.popSigElements()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("wrong parameters: %s", err.Error()))
|
|
|
|
}
|
2019-09-30 13:52:35 +00:00
|
|
|
// It's ok to have more keys than there are signatures (it would
|
|
|
|
// just mean that some keys didn't sign), but not the other way around.
|
2019-09-23 16:54:06 +00:00
|
|
|
if len(pkeys) < len(sigs) {
|
|
|
|
panic("more signatures than there are keys")
|
|
|
|
}
|
|
|
|
if v.checkhash == nil {
|
|
|
|
panic("VM is not set up properly for signature checks")
|
|
|
|
}
|
|
|
|
sigok := true
|
2019-09-30 13:52:35 +00:00
|
|
|
// j counts keys and i counts signatures.
|
2019-09-23 16:54:06 +00:00
|
|
|
j := 0
|
2019-09-30 13:52:35 +00:00
|
|
|
for i := 0; sigok && j < len(pkeys) && i < len(sigs); {
|
2019-09-23 16:54:06 +00:00
|
|
|
pkey := &keys.PublicKey{}
|
|
|
|
err := pkey.DecodeBytes(pkeys[j])
|
|
|
|
if err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
2019-09-30 13:52:35 +00:00
|
|
|
// We only move to the next signature if the check was
|
|
|
|
// successful, but if it's not maybe the next key will
|
|
|
|
// fit, so we always move to the next key.
|
2019-09-23 16:54:06 +00:00
|
|
|
if pkey.Verify(sigs[i], v.checkhash) {
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
j++
|
2019-09-30 13:52:35 +00:00
|
|
|
// When there are more signatures left to check than
|
|
|
|
// there are keys the check won't successed for sure.
|
|
|
|
if len(sigs)-i > len(pkeys)-j {
|
2019-09-23 16:54:06 +00:00
|
|
|
sigok = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v.estack.PushVal(sigok)
|
|
|
|
|
2019-09-24 12:06:23 +00:00
|
|
|
case NEWMAP:
|
|
|
|
v.estack.Push(&Element{value: NewMapItem()})
|
|
|
|
|
2019-09-24 12:41:39 +00:00
|
|
|
case KEYS:
|
|
|
|
item := v.estack.Pop()
|
|
|
|
if item == nil {
|
|
|
|
panic("no argument")
|
|
|
|
}
|
|
|
|
|
|
|
|
m, ok := item.value.(*MapItem)
|
|
|
|
if !ok {
|
|
|
|
panic("not a Map")
|
|
|
|
}
|
|
|
|
|
|
|
|
arr := make([]StackItem, 0, len(m.value))
|
|
|
|
for k := range m.value {
|
|
|
|
arr = append(arr, makeStackItem(k))
|
|
|
|
}
|
|
|
|
v.estack.PushVal(arr)
|
|
|
|
|
2019-09-24 12:53:55 +00:00
|
|
|
case VALUES:
|
|
|
|
item := v.estack.Pop()
|
|
|
|
if item == nil {
|
|
|
|
panic("no argument")
|
|
|
|
}
|
|
|
|
|
|
|
|
var arr []StackItem
|
|
|
|
switch t := item.value.(type) {
|
|
|
|
case *ArrayItem, *StructItem:
|
|
|
|
src := t.Value().([]StackItem)
|
|
|
|
arr = make([]StackItem, len(src))
|
|
|
|
for i := range src {
|
|
|
|
arr[i] = cloneIfStruct(src[i])
|
|
|
|
}
|
|
|
|
case *MapItem:
|
|
|
|
arr = make([]StackItem, 0, len(t.value))
|
|
|
|
for k := range t.value {
|
|
|
|
arr = append(arr, cloneIfStruct(t.value[k]))
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("not a Map, Array or Struct")
|
|
|
|
}
|
|
|
|
|
|
|
|
v.estack.PushVal(arr)
|
|
|
|
|
2019-09-24 12:25:57 +00:00
|
|
|
case HASKEY:
|
|
|
|
key := v.estack.Pop()
|
|
|
|
validateMapKey(key)
|
|
|
|
|
|
|
|
c := v.estack.Pop()
|
|
|
|
if c == nil {
|
|
|
|
panic("no value found")
|
|
|
|
}
|
|
|
|
switch t := c.value.(type) {
|
|
|
|
case *ArrayItem, *StructItem:
|
|
|
|
index := key.BigInt().Int64()
|
|
|
|
if index < 0 {
|
|
|
|
panic("negative index")
|
|
|
|
}
|
|
|
|
v.estack.PushVal(index < int64(len(c.Array())))
|
|
|
|
case *MapItem:
|
|
|
|
v.estack.PushVal(t.Has(key.value))
|
|
|
|
default:
|
|
|
|
panic("wrong collection type")
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
// Cryptographic operations.
|
2019-08-14 12:40:31 +00:00
|
|
|
case SHA1:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bytes()
|
|
|
|
sha := sha1.New()
|
|
|
|
sha.Write(b)
|
|
|
|
v.estack.PushVal(sha.Sum(nil))
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case SHA256:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bytes()
|
2019-08-23 15:50:45 +00:00
|
|
|
v.estack.PushVal(hash.Sha256(b).Bytes())
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case HASH160:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bytes()
|
2019-08-23 15:50:45 +00:00
|
|
|
v.estack.PushVal(hash.Hash160(b).Bytes())
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case HASH256:
|
2018-03-30 16:15:06 +00:00
|
|
|
b := v.estack.Pop().Bytes()
|
2019-08-23 15:50:45 +00:00
|
|
|
v.estack.PushVal(hash.DoubleSha256(b).Bytes())
|
2018-03-30 16:15:06 +00:00
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case NOP:
|
2018-03-30 16:15:06 +00:00
|
|
|
// unlucky ^^
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case THROW:
|
2018-03-30 16:15:06 +00:00
|
|
|
panic("THROW")
|
|
|
|
|
2019-08-14 12:40:31 +00:00
|
|
|
case THROWIFNOT:
|
2018-03-30 16:15:06 +00:00
|
|
|
if !v.estack.Pop().Bool() {
|
|
|
|
panic("THROWIFNOT")
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-02-09 15:53:58 +00:00
|
|
|
panic(fmt.Sprintf("unknown opcode %s", op.String()))
|
2018-03-30 16:15:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 12:53:55 +00:00
|
|
|
func cloneIfStruct(item StackItem) StackItem {
|
|
|
|
switch it := item.(type) {
|
|
|
|
case *StructItem:
|
|
|
|
return it.Clone()
|
|
|
|
default:
|
|
|
|
return it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-12 08:24:10 +00:00
|
|
|
func makeArrayOfFalses(n int) []StackItem {
|
|
|
|
items := make([]StackItem, n)
|
|
|
|
for i := range items {
|
|
|
|
items[i] = &BoolItem{false}
|
|
|
|
}
|
|
|
|
return items
|
|
|
|
}
|
|
|
|
|
2019-09-24 12:25:57 +00:00
|
|
|
func validateMapKey(key *Element) {
|
|
|
|
if key == nil {
|
|
|
|
panic("no key found")
|
|
|
|
}
|
|
|
|
switch key.value.(type) {
|
|
|
|
case *ArrayItem, *StructItem, *MapItem:
|
|
|
|
panic("key can't be a collection")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:15:06 +00:00
|
|
|
func init() {
|
|
|
|
log.SetPrefix("NEO-GO-VM > ")
|
|
|
|
log.SetFlags(0)
|
|
|
|
}
|