示例#1
0
class ExecutionEngine():

    _Table = None
    _Service = None

    _ScriptContainer = None
    _Crypto = None

    _VMState = VMState.BREAK

    _InvocationStack = None
    _EvaluationStack = None
    _AltStack = None

    @property
    def ScriptContainer(self):
        return self._ScriptContainer

    @property
    def Crypto(self):
        return self._Crypto

    @property
    def State(self):
        return self._VMState

    @property
    def InvocationStack(self):
        return self._InvocationStack

    @property
    def EvaluationStack(self):
        return self._EvaluationStack

    @property
    def AltStack(self):
        return self._AltStack

    @property
    def CurrentContext(self):
        return self._InvocationStack.Peek()

    @property
    def CallingContext(self):
        if self._InvocationStack.Count > 1:
            return self.InvocationStack.Peek(1)
        return None

    @property
    def EntryContext(self):
        return self.InvocationStack.Peek(self.InvocationStack.Count - 1)

    def __init__(self, container=None, crypto=None, table=None, service=None):
        self._ScriptContainer = container
        self._Crypto = crypto
        self._Table = table
        self._Service = service

        self._InvocationStack = RandomAccessStack(name='Invocation')
        self._EvaluationStack = RandomAccessStack(name='Evaluation')
        self._AltStack = RandomAccessStack(name='Alt')

    def AddBreakPoint(self, position):
        self.CurrentContext.Breakpoints.add(position)

    def Dispose(self):
        while self._InvocationStack.Count > 0:
            self._InvocationStack.Pop().Dispose()

    def Execute(self):
        self._VMState &= ~VMState.BREAK

        #        while (!State.HasFlag(VMState.HALT) & & !State.HasFlag(VMState.FAULT) & & !State.HasFlag(VMState.BREAK))
        #        StepInto();

        while self._VMState & VMState.HALT == 0 and self._VMState & VMState.FAULT == 0 and self._VMState & VMState.BREAK == 0:
            self.StepInto()

    def ExecuteOp(self, opcode, context):
        estack = self._EvaluationStack
        istack = self._InvocationStack
        astack = self._AltStack

        if opcode > PUSH16 and opcode != RET and context.PushOnly:
            self._VMState |= VMState.FAULT

        if opcode >= PUSHBYTES1 and opcode <= PUSHBYTES75:
            estack.PushT(
                context.OpReader.ReadBytes(int.from_bytes(opcode, 'little')))
        else:

            # push values
            pushops = [
                PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8,
                PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16
            ]

            if opcode == PUSH0:
                estack.PushT(bytearray([0]))

            elif opcode == PUSHDATA1:
                lenngth = context.OpReader.ReadByte()
                estack.PushT(bytearray(context.OpReader.ReadBytes(lenngth)))
            elif opcode == PUSHDATA2:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt16()))
            elif opcode == PUSHDATA4:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt32()))
            elif opcode in pushops:
                # EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1);
                topush = int.from_bytes(opcode, 'little') - int.from_bytes(
                    PUSH1, 'little') + 1
                estack.PushT(topush)

            #control
            elif opcode == NOP:
                pass
            elif opcode in [JMP, JMPIF, JMPIFNOT]:
                offset = context.OpReader.ReadInt16()
                offset = context.InstructionPointer + offset - 3
                if offset < 0 or offset > len(context.Script):
                    self._VMSTATE |= VMState.FAULT
                    return

                fValue = True
                if opcode > JMP:
                    fValue = estack.Pop().GetBoolean()
                    if opcode == JMPIFNOT:
                        fValue = not fValue
                if fValue:
                    context.SetInstructionPointer(offset)

            elif opcode == CALL:
                istack.PushT(context.Clone())
                context.SetInstructionPointer(context.InstructionPointer + 2)
                self.ExecuteOp(JMP, self.CurrentContext)

            elif opcode == RET:
                istack.Pop().Dispose()
                if istack.Count == 0:
                    self._VMState |= VMState.HALT

            elif opcode == APPCALL or opcode == TAILCALL:
                if self._Table == None:
                    self._VMState |= VMState.FAULT
                    return

                script_hash = UInt160(
                    data=context.OpReader.ReadBytes(20)).ToBytes()
                script = self._Table.GetScript(script_hash)

                if script == None:
                    self._VMState |= VMState.FAULT
                    return

                if opcode == TAILCALL:
                    istack.Pop().Dispose()

                self.LoadScript(script)

            elif opcode == SYSCALL:

                if not self._Service.Invoke(
                        context.OpReader.ReadVarBytes(252).decode('ascii'),
                        self):
                    self._VMState |= VMState.FAULT

            #stack operations
            elif opcode == DUPFROMALTSTACK:
                estack.PushT(astack.Peek())

            elif opcode == TOALTSTACK:
                astack.PushT(estack.Pop())

            elif opcode == FROMALTSTACK:
                estack.PushT(astack.Pop())

            elif opcode == XDROP:
                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return
                estack.Remove(n)

            elif opcode == XSWAP:
                n = estack.Pop().GetBigInteger()

                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                #if n == 0 break, same as do x if n > 0
                if n > 0:

                    item = estack.Peek(n)
                    estack.Set(n, estack.Peek())
                    estack.Set(0, item)

            elif opcode == XTUCK:
                n = estack.Pop().GetBigInteger()

                if n <= 0:
                    self._VMState |= VMState.FAULT
                    return

                estack.Insert(n, estack.Peek())

            elif opcode == DEPTH:
                estack.PushT(estack.Count)

            elif opcode == DROP:
                estack.Pop()

            elif opcode == DUP:
                estack.PushT(estack.Peek())

            elif opcode == NIP:
                x2 = estack.Pop()
                estack.Pop()
                estack.PushT(x2)

            elif opcode == OVER:

                x2 = estack.Pop()
                x1 = estack.Peek()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == PICK:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                estack.PushT(estack.Peek(n))

            elif opcode == ROLL:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                if n > 0:
                    estack.PushT(estack.Remove(n))

            elif opcode == ROT:
                x3 = estack.Pop()
                x2 = estack.Pop()
                x1 = estack.Pop()

                estack.PushT(x2)
                estack.PushT(x3)
                estack.PushT(x1)

            elif opcode == SWAP:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == TUCK:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)
                estack.PushT(x2)

            elif opcode == CAT:

                x2 = estack.Pop().GetByteArray()
                x1 = estack.Pop().GetByteArray()
                estack.PushT(x1 + x2)

            elif opcode == SUBSTR:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                index = estack.Pop().GetBigInteger()
                if index < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()
                estack.PushT(x[index:count])

            elif opcode == LEFT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()
                estack.PushT(x[:count])

            elif opcode == RIGHT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()
                if len(x) < count:
                    self._VMState |= VMState.FAULT
                    return

                estack.PushT(x[-count:])

            elif opcode == SIZE:

                x = estack.Pop().GetByteArray()
                estack.PushT(len(x))

            elif opcode == INVERT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(~x)

            elif opcode == AND:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 & x2)

            elif opcode == OR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 | x2)

            elif opcode == XOR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 ^ x2)

            elif opcode == EQUAL:
                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                res = x1.Equals(x2)
                estack.PushT(x1.Equals(x2))

            #numeric

            elif opcode == INC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x + 1)

            elif opcode == DEC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x - 1)

            elif opcode == SIGN:

                ##### Make sure to implement sign for big integer
                x = estack.Pop().GetBigInteger()
                estack.PushT(x.Sign)

            elif opcode == NEGATE:

                x = estack.Pop().GetBigInteger()
                estack.PushT(-x)

            elif opcode == ABS:

                x = estack.Pop().GetBigInteger()
                estack.PushT(abs(x))

            elif opcode == NOT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(not x)

            elif opcode == NZ:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x is not 0)

            elif opcode == ADD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 + x2)

            elif opcode == SUB:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                #                print("x2, x1 %s .. %s " % (x2, x1))
                estack.PushT(x1 - x2)

            elif opcode == MUL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 * x2)

            elif opcode == DIV:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 / x2)

            elif opcode == MOD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 % x2)

            elif opcode == SHL:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x << n)

            elif opcode == SHR:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x >> n)

            elif opcode == BOOLAND:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 and x2)

            elif opcode == BOOLOR:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 or x2)

            elif opcode == NUMEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 == x2)

            elif opcode == NUMNOTEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 != x2)

            elif opcode == LT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 < x2)

            elif opcode == GT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 > x2)

            elif opcode == LTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 <= x2)

            elif opcode == GTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 >= x2)

            elif opcode == MIN:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(min(x1, x2))

            elif opcode == MAX:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(max(x1, x2))

            elif opcode == WITHIN:

                b = estack.Pop().GetBigInteger()
                a = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(a <= x and x < b)

            #CRyPTO
            elif opcode == SHA1:
                h = hashlib.sha1(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == SHA256:
                h = hashlib.sha256(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == HASH160:

                estack.PushT(self.Crypto.Hash160(estack.Pop().GetByteArray()))

            elif opcode == HASH256:

                estack.PushT(self.Crypto.Hash256(estack.Pop().GetByteArray()))

            elif opcode == CHECKSIG:

                pubkey = estack.Pop().GetByteArray()
                sig = estack.Pop().GetByteArray()

                try:

                    self.Crypto.VerifySignature(
                        self.ScriptContainer.GetMessage(), pubkey, sig)

                except Exception as e:

                    estack.PushT(False)

            elif opcode == CHECKMULTISIG:

                n = estack.Pop().GetBigInteger()

                if n < 1:
                    self._VMState |= VMState.FAULT
                    return

                pubkeys = []
                for i in range(0, n):
                    pubkeys[i] = estack.Pop().GetByteArray()

                m = estack.Pop().GetBigInteger()

                if m < 1 or m > n:
                    self._VMState |= VMState.FAULT
                    return

                sigs = []

                for i in range(0, m):
                    m[i] = estack.Pop().GetByteArray()

                message = self.ScriptContainer.GetMessage()

                fSuccess = True

                try:

                    i = 0
                    j = 0

                    while fSuccess and i < m and j < n:

                        if self.Crypto.VerifySignature(message, sigs[i],
                                                       pubkeys[j]):
                            i += 1
                        j += 1

                        if m - i > j - n:
                            fSuccess = False

                except Exception as e:
                    fSuccess = False

                estack.PushT(fSuccess)

            #lists
            elif opcode == ARRAYSIZE:

                item = estack.Pop()

                if not item.IsArray:
                    estack.PushT(len(item.GetByteArray()))

                else:
                    estack.PushT(len(item.GetArray()))

            elif opcode == PACK:

                size = estack.Pop().GetBigInteger()

                if size < 0 or size > estack.Count:
                    self._VMState |= VMState.FAULT
                    return

                items = []

                for i in range(0, size):
                    items.append(estack.Pop())

                estack.PushT(items)

            elif opcode == UNPACK:
                item = estack.Pop()

                if not item.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = item.GetArray()
                items.reverse()

                [estack.PushT(i) for i in items]

                estack.PushT(len(items))

            elif opcode == PICKITEM:

                index = estack.Pop().GetBigInteger()

                if index < 0:
                    self._VMState |= VMState.FAULT
                    return

                item = estack.Pop()

                if not item.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = item.GetArray()

                if index >= len(items):
                    self._VMState |= VMState.FAULT
                    return

                estack.PushT(items[index])

            elif opcode == SETITEM:

                newItem = estack.Pop()

                if newItem.IsStruct:
                    newItem = newItem.Clone()

                index = estack.Pop().GetBigInteger()

                arrItem = estack.Pop()

                if not arrItem.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = arrItem.GetArray()

                if index < 0 or index >= len(items):
                    self._VMState |= VMState.FAULT
                    return

                items[index] = newItem

            elif opcode == NEWARRAY:

                count = estack.Pop().GetBigInteger()

                items = [None for i in range(0, count)]

                estack.PushT(Array(items))

            elif opcode == NEWSTRUCT:

                count = estack.Pop().GetBigInteger()

                items = [None for i in range(0, count)]

                estack.PushT(Struct(items))

            else:

                self._VMState |= VMState.FAULT
                return

        if self._VMState & VMState.FAULT == 0 and self.InvocationStack.Count > 0:

            if self.CurrentContext.InstructionPointer in self.CurrentContext.Breakpoints:
                self._VMState |= VMState.BREAK

    def LoadScript(self, script, push_only=False):

        context = ExecutionContext(self, script, push_only)
        self._InvocationStack.PushT(context)

    def RemoveBreakPoint(self, position):

        if self._InvocationStack.Count == 0:
            return False
        return self.CurrentContext.Breakpoints.remove(position)

    def StepInto(self):
        if self._InvocationStack.Count == 0:
            self._VMState |= VMState.HALT

        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            self.__log.debug("stopping because vm state is %s " %
                             self._VMState)

        op = None

        if self.CurrentContext.InstructionPointer >= len(
                self.CurrentContext.Script):
            op = RET
        else:
            op = self.CurrentContext.OpReader.ReadByte(do_ord=False)

        opname = ToName(op)
        #        print("____________________________________________________")
        #        print("%s -> %s" % (op, opname))
        #        print("-----------------------------------")

        try:
            self.ExecuteOp(op, self.CurrentContext)
        except Exception as e:
            print("could not execute op %s " % e)
            traceback.print_stack()
            traceback.print_exc()
            raise e

    def StepOut(self):
        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while   self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:

            self.StepInto()

    def StepOver(self):
        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            return

        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while   self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:

            self.StepInto()
示例#2
0
class ExecutionEngine():

    _Table = None
    _Service = None

    _ScriptContainer = None
    _Crypto = None

    _VMState = VMState.BREAK

    _InvocationStack = None
    _EvaluationStack = None
    _AltStack = None

    _ExecutedScriptHashes = None

    ops_processed = 0

    @property
    def ScriptContainer(self):
        return self._ScriptContainer

    @property
    def Crypto(self):
        return self._Crypto

    @property
    def State(self):
        return self._VMState

    @property
    def InvocationStack(self):
        return self._InvocationStack

    @property
    def EvaluationStack(self):
        return self._EvaluationStack

    @property
    def AltStack(self):
        return self._AltStack

    @property
    def CurrentContext(self):
        return self._InvocationStack.Peek()

    @property
    def CallingContext(self):
        if self._InvocationStack.Count > 1:
            return self.InvocationStack.Peek(1)
        return None

    @property
    def EntryContext(self):
        return self.InvocationStack.Peek(self.InvocationStack.Count - 1)

    @property
    def ExecutedScriptHashes(self):
        return self._ExecutedScriptHashes

    def __init__(self, container=None, crypto=None, table=None, service=None):
        self._ScriptContainer = container
        self._Crypto = crypto
        self._Table = table
        self._Service = service

        self._InvocationStack = RandomAccessStack(name='Invocation')
        self._EvaluationStack = RandomAccessStack(name='Evaluation')
        self._AltStack = RandomAccessStack(name='Alt')
        self._ExecutedScriptHashes = []
        self.ops_processed = 0

    def AddBreakPoint(self, position):
        self.CurrentContext.Breakpoints.add(position)

    def ResultsForCode(self, contract):
        try:
            return_type = ContractParameterType(contract.ReturnType)

            item = self.EvaluationStack.Items[0]
            if return_type == ContractParameterType.Integer:
                return item.GetBigInteger()
            elif return_type == ContractParameterType.Boolean:
                return item.GetBoolean()
            elif return_type == ContractParameterType.ByteArray:
                return item.GetByteArray()
            elif return_type == ContractParameterType.String:
                return item.GetString()
            elif return_type == ContractParameterType.Array:
                return item.GetArray()
            else:
                logger.error("Could not format results for return type %s " %
                             return_type)
            return item
        except Exception as e:
            pass

        return self.EvaluationStack.Items

    def Dispose(self):
        while self._InvocationStack.Count > 0:
            self._InvocationStack.Pop().Dispose()

    def Execute(self):
        self._VMState &= ~VMState.BREAK

        while self._VMState & VMState.HALT == 0 and self._VMState & VMState.FAULT == 0 and self._VMState & VMState.BREAK == 0:
            self.StepInto()

    def ExecuteOp(self, opcode, context):
        estack = self._EvaluationStack
        istack = self._InvocationStack
        astack = self._AltStack

        if opcode > PUSH16 and opcode != RET and context.PushOnly:
            self._VMState |= VMState.FAULT

        if opcode >= PUSHBYTES1 and opcode <= PUSHBYTES75:
            bytestoread = context.OpReader.ReadBytes(
                int.from_bytes(opcode, 'little'))
            estack.PushT(bytestoread)
        else:

            # push values
            pushops = [
                PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8,
                PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16
            ]

            if opcode == PUSH0:
                estack.PushT(bytearray([0]))

            elif opcode == PUSHDATA1:
                lenngth = context.OpReader.ReadByte()
                estack.PushT(bytearray(context.OpReader.ReadBytes(lenngth)))
            elif opcode == PUSHDATA2:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt16()))
            elif opcode == PUSHDATA4:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt32()))
            elif opcode in pushops:
                # EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1);
                topush = int.from_bytes(opcode, 'little') - int.from_bytes(
                    PUSH1, 'little') + 1
                estack.PushT(topush)

            # control
            elif opcode == NOP:
                pass
            elif opcode in [JMP, JMPIF, JMPIFNOT]:
                offset_b = context.OpReader.ReadInt16()
                offset = context.InstructionPointer + offset_b - 3

                if offset < 0 or offset > len(context.Script):
                    self._VMState |= VMState.FAULT
                    return

                fValue = True
                if opcode > JMP:
                    fValue = estack.Pop().GetBoolean()
                    if opcode == JMPIFNOT:
                        fValue = not fValue
                if fValue:
                    context.SetInstructionPointer(offset)

            elif opcode == CALL:
                istack.PushT(context.Clone())
                context.SetInstructionPointer(context.InstructionPointer + 2)

                self.ExecuteOp(JMP, self.CurrentContext)

            elif opcode == RET:
                istack.Pop().Dispose()
                if istack.Count == 0:
                    self._VMState |= VMState.HALT

            elif opcode == APPCALL or opcode == TAILCALL:
                if self._Table is None:
                    self._VMState |= VMState.FAULT
                    return

                script_hash = context.OpReader.ReadBytes(20)

                is_normal_call = False
                for b in script_hash:
                    if b > 0:
                        is_normal_call = True

                if not is_normal_call:
                    script_hash = self.EvaluationStack.Pop().GetByteArray()

                script = self._Table.GetScript(
                    UInt160(data=script_hash).ToBytes())

                if script is None:
                    logger.error(
                        "Could not find script from script table: %s " %
                        script_hash)
                    self._VMState |= VMState.FAULT
                    return

                if opcode == TAILCALL:
                    istack.Pop().Dispose()

                self.LoadScript(script)

            elif opcode == SYSCALL:
                call = context.OpReader.ReadVarBytes(252).decode('ascii')
                if not self._Service.Invoke(call, self):
                    self._VMState |= VMState.FAULT

            # stack operations
            elif opcode == DUPFROMALTSTACK:
                estack.PushT(astack.Peek())

            elif opcode == TOALTSTACK:
                astack.PushT(estack.Pop())

            elif opcode == FROMALTSTACK:
                estack.PushT(astack.Pop())

            elif opcode == XDROP:
                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return
                estack.Remove(n)

            elif opcode == XSWAP:
                n = estack.Pop().GetBigInteger()

                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                # if n == 0 break, same as do x if n > 0
                if n > 0:

                    item = estack.Peek(n)
                    estack.Set(n, estack.Peek())
                    estack.Set(0, item)

            elif opcode == XTUCK:
                n = estack.Pop().GetBigInteger()

                if n <= 0:
                    self._VMState |= VMState.FAULT
                    return

                estack.Insert(n, estack.Peek())

            elif opcode == DEPTH:
                estack.PushT(estack.Count)

            elif opcode == DROP:
                estack.Pop()

            elif opcode == DUP:
                estack.PushT(estack.Peek())

            elif opcode == NIP:
                x2 = estack.Pop()
                estack.Pop()
                estack.PushT(x2)

            elif opcode == OVER:

                x2 = estack.Pop()
                x1 = estack.Peek()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == PICK:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                estack.PushT(estack.Peek(n))

            elif opcode == ROLL:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return

                if n > 0:
                    estack.PushT(estack.Remove(n))

            elif opcode == ROT:
                x3 = estack.Pop()
                x2 = estack.Pop()
                x1 = estack.Pop()

                estack.PushT(x2)
                estack.PushT(x3)
                estack.PushT(x1)

            elif opcode == SWAP:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == TUCK:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)
                estack.PushT(x2)

            elif opcode == CAT:

                x2 = estack.Pop().GetByteArray()
                x1 = estack.Pop().GetByteArray()
                estack.PushT(x1 + x2)

            elif opcode == SUBSTR:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                index = estack.Pop().GetBigInteger()
                if index < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()

                estack.PushT(x[index:count + index])

            elif opcode == LEFT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()
                estack.PushT(x[:count])

            elif opcode == RIGHT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    self._VMState |= VMState.FAULT
                    return

                x = estack.Pop().GetByteArray()
                if len(x) < count:
                    self._VMState |= VMState.FAULT
                    return

                estack.PushT(x[-count:])

            elif opcode == SIZE:

                x = estack.Pop().GetByteArray()
                estack.PushT(len(x))

            elif opcode == INVERT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(~x)

            elif opcode == AND:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 & x2)

            elif opcode == OR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 | x2)

            elif opcode == XOR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 ^ x2)

            elif opcode == EQUAL:
                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x1.Equals(x2))

            # numeric

            elif opcode == INC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x + 1)

            elif opcode == DEC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x - 1)

            elif opcode == SIGN:

                # Make sure to implement sign for big integer
                x = estack.Pop().GetBigInteger()
                estack.PushT(x.Sign)

            elif opcode == NEGATE:

                x = estack.Pop().GetBigInteger()
                estack.PushT(-x)

            elif opcode == ABS:

                x = estack.Pop().GetBigInteger()
                estack.PushT(abs(x))

            elif opcode == NOT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(not x)

            elif opcode == NZ:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x is not 0)

            elif opcode == ADD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 + x2)

            elif opcode == SUB:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 - x2)

            elif opcode == MUL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 * x2)

            elif opcode == DIV:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 / x2)

            elif opcode == MOD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 % x2)

            elif opcode == SHL:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x << n)

            elif opcode == SHR:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x >> n)

            elif opcode == BOOLAND:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 and x2)

            elif opcode == BOOLOR:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 or x2)

            elif opcode == NUMEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x2 == x1)

            elif opcode == NUMNOTEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 != x2)

            elif opcode == LT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 < x2)

            elif opcode == GT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 > x2)

            elif opcode == LTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 <= x2)

            elif opcode == GTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 >= x2)

            elif opcode == MIN:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(min(x1, x2))

            elif opcode == MAX:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(max(x1, x2))

            elif opcode == WITHIN:

                b = estack.Pop().GetBigInteger()
                a = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(a <= x and x < b)

            # CRyPTO
            elif opcode == SHA1:
                h = hashlib.sha1(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == SHA256:
                h = hashlib.sha256(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == HASH160:

                estack.PushT(self.Crypto.Hash160(estack.Pop().GetByteArray()))

            elif opcode == HASH256:

                estack.PushT(self.Crypto.Hash256(estack.Pop().GetByteArray()))

            elif opcode == CHECKSIG:

                pubkey = estack.Pop().GetByteArray()
                sig = estack.Pop().GetByteArray()

                try:

                    res = self.Crypto.VerifySignature(
                        self.ScriptContainer.GetMessage(), sig, pubkey)
                    estack.PushT(res)

                except Exception as e:
                    estack.PushT(False)
                    logger.error("Could not checksig: %s " % e)

            elif opcode == CHECKMULTISIG:

                n = estack.Pop().GetBigInteger()

                if n < 1:
                    self._VMState |= VMState.FAULT
                    return

                pubkeys = []
                for i in range(0, n):
                    pubkeys.append(estack.Pop().GetByteArray())

                m = estack.Pop().GetBigInteger()

                if m < 1 or m > n:
                    self._VMState |= VMState.FAULT
                    return

                sigs = []

                for i in range(0, m):
                    sigs.append(estack.Pop().GetByteArray())

                message = self.ScriptContainer.GetMessage(
                ) if self.ScriptContainer else ''

                fSuccess = True

                try:

                    i = 0
                    j = 0

                    while fSuccess and i < m and j < n:

                        if self.Crypto.VerifySignature(message, sigs[i],
                                                       pubkeys[j]):
                            i += 1
                        j += 1

                        if m - i > n - j:
                            fSuccess = False

                except Exception as e:
                    fSuccess = False

                estack.PushT(fSuccess)

            # lists
            elif opcode == ARRAYSIZE:

                item = estack.Pop()

                if not item:
                    self._VMState |= VMState.FAULT
                    return

                if not item.IsArray:
                    estack.PushT(len(item.GetByteArray()))

                else:
                    estack.PushT(len(item.GetArray()))

            elif opcode == PACK:

                size = estack.Pop().GetBigInteger()

                if size < 0 or size > estack.Count:
                    self._VMState |= VMState.FAULT
                    return

                items = []

                for i in range(0, size):
                    topack = estack.Pop()
                    items.append(topack)

                estack.PushT(items)

            elif opcode == UNPACK:
                item = estack.Pop()

                if not item.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = item.GetArray()
                items.reverse()

                [estack.PushT(i) for i in items]

                estack.PushT(len(items))

            elif opcode == PICKITEM:

                index = estack.Pop().GetBigInteger()

                if index < 0:
                    self._VMState |= VMState.FAULT
                    return

                item = estack.Pop()

                if not item.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = item.GetArray()

                if index >= len(items):
                    self._VMState |= VMState.FAULT
                    return

                to_pick = items[index]
                estack.PushT(to_pick)

            elif opcode == SETITEM:
                newItem = estack.Pop()

                if issubclass(type(newItem), StackItem) and newItem.IsStruct:
                    newItem = newItem.Clone()

                index = estack.Pop().GetBigInteger()

                arrItem = estack.Pop()

                if not issubclass(type(arrItem),
                                  StackItem) or not arrItem.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                items = arrItem.GetArray()

                if index < 0 or index >= len(items):
                    self._VMState |= VMState.FAULT
                    return

                items[index] = newItem

            elif opcode == NEWARRAY:

                count = estack.Pop().GetBigInteger()
                items = [None for i in range(0, count)]
                estack.PushT(Array(items))

            elif opcode == NEWSTRUCT:

                count = estack.Pop().GetBigInteger()

                items = [None for i in range(0, count)]

                estack.PushT(Struct(items))

            elif opcode == APPEND:
                newItem = estack.Pop()

                if type(newItem) is Struct:
                    newItem = newItem.Clone()

                arrItem = estack.Pop()

                if not arrItem.IsArray:
                    self._VMState |= VMState.FAULT
                    return

                arr = arrItem.GetArray()
                arr.append(newItem)

            elif opcode == REVERSE:

                arrItem = estack.Pop()
                if not arrItem.IsArray:
                    self._VMState |= VMState.FAULT
                    return
                arrItem.GetArray().reverse()

            elif opcode == REMOVE:
                index = estack.Pop().GetBigInteger()
                arrItem = estack.Pop()
                if not arrItem.IsArray:
                    self._VMState |= VMState.FAULT
                    return
                items = arrItem.GetArray()

                if index < 0 or index >= len(items):
                    self._VMState |= VMState.FAULT
                    return
                del items[index]

            elif opcode == THROW:
                self._VMState |= VMState.FAULT
                return

            elif opcode == THROWIFNOT:
                if not estack.Pop().GetBoolean():
                    self._VMState |= VMState.FAULT
                    return

            elif opcode == DEBUG:
                pdb.set_trace()
                return

            else:

                self._VMState |= VMState.FAULT
                return

        if self._VMState & VMState.FAULT == 0 and self.InvocationStack.Count > 0:

            if self.CurrentContext.InstructionPointer in self.CurrentContext.Breakpoints:
                self._VMState |= VMState.BREAK

    def LoadScript(self, script, push_only=False):

        context = ExecutionContext(self, script, push_only)
        self._InvocationStack.PushT(context)

        self._ExecutedScriptHashes.append(context.ScriptHash())

    def RemoveBreakPoint(self, position):

        if self._InvocationStack.Count == 0:
            return False
        return self.CurrentContext.Breakpoints.remove(position)

    def StepInto(self):
        if self._InvocationStack.Count == 0:
            logger.info("INVOCATION COUNT IS 0, HALT")
            self._VMState |= VMState.HALT

        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            logger.info("stopping because vm state is %s " % self._VMState)
            return

        op = None

        if self.CurrentContext.InstructionPointer >= len(
                self.CurrentContext.Script):
            op = RET
        else:
            op = self.CurrentContext.OpReader.ReadByte(do_ord=False)

#        opname = ToName(op)
#        logger.info("____________________________________________________")
#        logger.info("[%s] %02x -> %s" % (self.ops_processed,int.from_bytes(op,byteorder='little'), opname))
#        logger.info("-----------------------------------")

        self.ops_processed += 1

        try:
            self.ExecuteOp(op, self.CurrentContext)
        except Exception as e:
            logger.error("COULD NOT EXECUTE OP: %s %s %s" %
                         (e, op, ToName(op)))
            logger.exception(e)

    def StepOut(self):
        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:

            self.StepInto()

    def StepOver(self):
        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            return

        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:

            self.StepInto()
示例#3
0
class ExecutionEngine():
    _Table = None
    _Service = None

    _ScriptContainer = None
    _Crypto = None

    _VMState = None

    _InvocationStack = None
    _EvaluationStack = None
    _AltStack = None

    _ExecutedScriptHashes = None

    ops_processed = 0

    _exit_on_error = False

    log_file_name = 'vm_instructions.log'
    # file descriptor
    log_file = None
    _is_write_log = False

    _debug_map = None
    _vm_debugger = None

    def write_log(self, message):
        """
        Write a line to the VM instruction log file.

        Args:
            message (str): string message to write to file.
        """
        if self._is_write_log and self.log_file and not self.log_file.closed:
            self.log_file.write(message + '\n')

    @property
    def ScriptContainer(self):
        return self._ScriptContainer

    @property
    def Crypto(self):
        return self._Crypto

    @property
    def State(self):
        return self._VMState

    @property
    def InvocationStack(self):
        return self._InvocationStack

    @property
    def EvaluationStack(self):
        return self._EvaluationStack

    @property
    def AltStack(self):
        return self._AltStack

    @property
    def CurrentContext(self):
        return self._InvocationStack.Peek()

    @property
    def CallingContext(self):
        if self._InvocationStack.Count > 1:
            return self.InvocationStack.Peek(1)
        return None

    @property
    def ExitOnError(self):
        return self._exit_on_error

    @property
    def EntryContext(self):
        return self.InvocationStack.Peek(self.InvocationStack.Count - 1)

    @property
    def ExecutedScriptHashes(self):
        return self._ExecutedScriptHashes

    def __init__(self,
                 container=None,
                 crypto=None,
                 table=None,
                 service=None,
                 exit_on_error=False):
        self._VMState = VMState.BREAK
        self._ScriptContainer = container
        self._Crypto = crypto
        self._Table = table
        self._Service = service
        self._exit_on_error = exit_on_error
        self._InvocationStack = RandomAccessStack(name='Invocation')
        self._EvaluationStack = RandomAccessStack(name='Evaluation')
        self._AltStack = RandomAccessStack(name='Alt')
        self._ExecutedScriptHashes = []
        self.ops_processed = 0
        self._debug_map = None
        self._is_write_log = settings.log_vm_instructions

    def AddBreakPoint(self, position):
        self.CurrentContext.Breakpoints.add(position)

    def LoadDebugInfoForScriptHash(self, debug_map, script_hash):
        if debug_map and script_hash:
            self._debug_map = debug_map
            self._debug_map['script_hash'] = script_hash

    def Dispose(self):
        while self._InvocationStack.Count > 0:
            self._InvocationStack.Pop().Dispose()

    def Execute(self):
        self._VMState &= ~VMState.BREAK

        def loop_stepinto():
            while self._VMState & VMState.HALT == 0 and self._VMState & VMState.FAULT == 0 and self._VMState & VMState.BREAK == 0:
                self.StepInto()

        if settings.log_vm_instructions:
            with open(self.log_file_name, 'w') as self.log_file:
                self.write_log(str(datetime.datetime.now()))
                loop_stepinto()
        else:
            loop_stepinto()

    def ExecuteOp(self, opcode, context):
        estack = self._EvaluationStack
        istack = self._InvocationStack
        astack = self._AltStack

        if opcode > PUSH16 and opcode != RET and context.PushOnly:
            return self.VM_FAULT_and_report(VMFault.UNKNOWN1)

        if opcode >= PUSHBYTES1 and opcode <= PUSHBYTES75:
            bytestoread = context.OpReader.ReadBytes(
                int.from_bytes(opcode, 'little'))
            estack.PushT(bytestoread)
        else:

            # push values
            pushops = [
                PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8,
                PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16
            ]

            if opcode == PUSH0:
                estack.PushT(bytearray(0))

            elif opcode == PUSHDATA1:
                lenngth = context.OpReader.ReadByte()
                estack.PushT(bytearray(context.OpReader.ReadBytes(lenngth)))
            elif opcode == PUSHDATA2:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt16()))
            elif opcode == PUSHDATA4:
                estack.PushT(
                    context.OpReader.ReadBytes(context.OpReader.ReadUInt32()))
            elif opcode in pushops:
                # EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + 1);
                topush = int.from_bytes(opcode, 'little') - int.from_bytes(
                    PUSH1, 'little') + 1
                estack.PushT(topush)

            # control
            elif opcode == NOP:
                pass
            elif opcode in [JMP, JMPIF, JMPIFNOT]:
                offset_b = context.OpReader.ReadInt16()
                offset = context.InstructionPointer + offset_b - 3

                if offset < 0 or offset > len(context.Script):
                    return self.VM_FAULT_and_report(VMFault.INVALID_JUMP)

                fValue = True
                if opcode > JMP:
                    fValue = estack.Pop().GetBoolean()
                    if opcode == JMPIFNOT:
                        fValue = not fValue
                if fValue:
                    context.SetInstructionPointer(offset)

            elif opcode == CALL:
                istack.PushT(context.Clone())
                context.SetInstructionPointer(context.InstructionPointer + 2)

                self.ExecuteOp(JMP, self.CurrentContext)

            elif opcode == RET:
                istack.Pop().Dispose()
                if istack.Count == 0:
                    self._VMState |= VMState.HALT

            elif opcode == APPCALL or opcode == TAILCALL:
                if self._Table is None:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN2)

                script_hash = context.OpReader.ReadBytes(20)

                is_normal_call = False
                for b in script_hash:
                    if b > 0:
                        is_normal_call = True

                if not is_normal_call:
                    script_hash = self.EvaluationStack.Pop().GetByteArray()

                script = self._Table.GetScript(
                    UInt160(data=script_hash).ToBytes())

                if script is None:
                    logger.error(
                        "Could not find script from script table: %s " %
                        script_hash)
                    return self.VM_FAULT_and_report(VMFault.INVALID_CONTRACT,
                                                    script_hash)

                if opcode == TAILCALL:
                    istack.Pop().Dispose()

                self.LoadScript(script)

            elif opcode == SYSCALL:
                call = context.OpReader.ReadVarBytes(252).decode('ascii')
                self.write_log(call)
                if not self._Service.Invoke(call, self):
                    return self.VM_FAULT_and_report(VMFault.SYSCALL_ERROR,
                                                    call)

            # stack operations
            elif opcode == DUPFROMALTSTACK:
                estack.PushT(astack.Peek())

            elif opcode == TOALTSTACK:
                astack.PushT(estack.Pop())

            elif opcode == FROMALTSTACK:
                estack.PushT(astack.Pop())

            elif opcode == XDROP:
                n = estack.Pop().GetBigInteger()
                if n < 0:
                    self._VMState |= VMState.FAULT
                    return
                estack.Remove(n)

            elif opcode == XSWAP:
                n = estack.Pop().GetBigInteger()

                if n < 0:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN3)

                # if n == 0 break, same as do x if n > 0
                if n > 0:
                    item = estack.Peek(n)
                    estack.Set(n, estack.Peek())
                    estack.Set(0, item)

            elif opcode == XTUCK:
                n = estack.Pop().GetBigInteger()

                if n <= 0:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN4)

                estack.Insert(n, estack.Peek())

            elif opcode == DEPTH:
                estack.PushT(estack.Count)

            elif opcode == DROP:
                estack.Pop()

            elif opcode == DUP:
                estack.PushT(estack.Peek())

            elif opcode == NIP:
                x2 = estack.Pop()
                estack.Pop()
                estack.PushT(x2)

            elif opcode == OVER:

                x2 = estack.Pop()
                x1 = estack.Peek()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == PICK:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN5)

                estack.PushT(estack.Peek(n))

            elif opcode == ROLL:

                n = estack.Pop().GetBigInteger()
                if n < 0:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN6)

                if n > 0:
                    estack.PushT(estack.Remove(n))

            elif opcode == ROT:
                x3 = estack.Pop()
                x2 = estack.Pop()
                x1 = estack.Pop()

                estack.PushT(x2)
                estack.PushT(x3)
                estack.PushT(x1)

            elif opcode == SWAP:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)

            elif opcode == TUCK:

                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x2)
                estack.PushT(x1)
                estack.PushT(x2)

            elif opcode == CAT:

                x2 = estack.Pop().GetByteArray()
                x1 = estack.Pop().GetByteArray()
                estack.PushT(x1 + x2)

            elif opcode == SUBSTR:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    return self.VM_FAULT_and_report(
                        VMFault.SUBSTR_INVALID_LENGTH)

                index = estack.Pop().GetBigInteger()
                if index < 0:
                    return self.VM_FAULT_and_report(
                        VMFault.SUBSTR_INVALID_INDEX)

                x = estack.Pop().GetByteArray()

                estack.PushT(x[index:count + index])

            elif opcode == LEFT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    return self.VM_FAULT_and_report(VMFault.LEFT_INVALID_COUNT)

                x = estack.Pop().GetByteArray()
                estack.PushT(x[:count])

            elif opcode == RIGHT:

                count = estack.Pop().GetBigInteger()
                if count < 0:
                    return self.VM_FAULT_and_report(
                        VMFault.RIGHT_INVALID_COUNT)

                x = estack.Pop().GetByteArray()
                if len(x) < count:
                    return self.VM_FAULT_and_report(VMFault.RIGHT_UNKNOWN)

                estack.PushT(x[-count:])

            elif opcode == SIZE:

                x = estack.Pop().GetByteArray()
                estack.PushT(len(x))

            elif opcode == INVERT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(~x)

            elif opcode == AND:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                estack.PushT(x1 & x2)

            elif opcode == OR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 | x2)

            elif opcode == XOR:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 ^ x2)

            elif opcode == EQUAL:
                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x1.Equals(x2))

            # numeric

            elif opcode == INC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x + 1)

            elif opcode == DEC:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x - 1)

            elif opcode == SIGN:

                # Make sure to implement sign for big integer
                x = estack.Pop().GetBigInteger()
                estack.PushT(x.Sign)

            elif opcode == NEGATE:

                x = estack.Pop().GetBigInteger()
                estack.PushT(-x)

            elif opcode == ABS:

                x = estack.Pop().GetBigInteger()
                estack.PushT(abs(x))

            elif opcode == NOT:

                x = estack.Pop().GetBigInteger()
                estack.PushT(not x)

            elif opcode == NZ:

                x = estack.Pop().GetBigInteger()
                estack.PushT(x is not 0)

            elif opcode == ADD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                estack.PushT(x1 + x2)

            elif opcode == SUB:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 - x2)

            elif opcode == MUL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 * x2)

            elif opcode == DIV:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 / x2)

            elif opcode == MOD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 % x2)

            elif opcode == SHL:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x << n)

            elif opcode == SHR:

                n = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(x >> n)

            elif opcode == BOOLAND:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 and x2)

            elif opcode == BOOLOR:

                x2 = estack.Pop().GetBoolean()
                x1 = estack.Pop().GetBoolean()

                estack.PushT(x1 or x2)

            elif opcode == NUMEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x2 == x1)

            elif opcode == NUMNOTEQUAL:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 != x2)

            elif opcode == LT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 < x2)

            elif opcode == GT:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 > x2)

            elif opcode == LTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 <= x2)

            elif opcode == GTE:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(x1 >= x2)

            elif opcode == MIN:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                estack.PushT(min(x1, x2))

            elif opcode == MAX:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()

                estack.PushT(max(x1, x2))

            elif opcode == WITHIN:

                b = estack.Pop().GetBigInteger()
                a = estack.Pop().GetBigInteger()
                x = estack.Pop().GetBigInteger()

                estack.PushT(a <= x and x < b)

            # CRyPTO
            elif opcode == SHA1:
                h = hashlib.sha1(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == SHA256:
                h = hashlib.sha256(estack.Pop().GetByteArray())
                estack.PushT(h.digest())

            elif opcode == HASH160:

                estack.PushT(self.Crypto.Hash160(estack.Pop().GetByteArray()))

            elif opcode == HASH256:

                estack.PushT(self.Crypto.Hash256(estack.Pop().GetByteArray()))

            elif opcode == CHECKSIG:

                pubkey = estack.Pop().GetByteArray()
                sig = estack.Pop().GetByteArray()
                try:
                    res = self.Crypto.VerifySignature(
                        self.ScriptContainer.GetMessage(), sig, pubkey)
                    estack.PushT(res)
                except Exception as e:
                    estack.PushT(False)
                    logger.error("Could not checksig: %s " % e)

            elif opcode == VERIFY:
                pubkey = estack.Pop().GetByteArray()
                sig = estack.Pop().GetByteArray()
                message = estack.Pop().GetString()
                try:
                    res = self.Crypto.VerifySignature(message, sig, pubkey)
                    estack.PushT(res)
                except Exception as e:
                    estack.PushT(False)
                    logger.error("Could not checksig: %s " % e)

            elif opcode == CHECKMULTISIG:

                n = estack.Pop().GetBigInteger()

                if n < 1:
                    return self.VM_FAULT_and_report(
                        VMFault.CHECKMULTISIG_INVALID_PUBLICKEY_COUNT)

                pubkeys = []
                for i in range(0, n):
                    pubkeys.append(estack.Pop().GetByteArray())

                m = estack.Pop().GetBigInteger()

                if m < 1 or m > n:
                    return self.VM_FAULT_and_report(
                        VMFault.CHECKMULTISIG_SIGNATURE_ERROR, m, n)

                sigs = []

                for i in range(0, m):
                    sigs.append(estack.Pop().GetByteArray())

                message = self.ScriptContainer.GetMessage(
                ) if self.ScriptContainer else ''

                fSuccess = True

                try:

                    i = 0
                    j = 0

                    while fSuccess and i < m and j < n:

                        if self.Crypto.VerifySignature(message, sigs[i],
                                                       pubkeys[j]):
                            i += 1
                        j += 1

                        if m - i > n - j:
                            fSuccess = False

                except Exception as e:
                    fSuccess = False

                estack.PushT(fSuccess)

            # lists
            elif opcode == ARRAYSIZE:

                item = estack.Pop()

                if not item:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN7)

                if isinstance(item, CollectionMixin):
                    estack.PushT(item.Count)

                else:
                    estack.PushT(len(item.GetByteArray()))

            elif opcode == PACK:

                size = estack.Pop().GetBigInteger()

                if size < 0 or size > estack.Count:
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN8)

                items = []

                for i in range(0, size):
                    topack = estack.Pop()
                    items.append(topack)

                estack.PushT(items)

            elif opcode == UNPACK:
                item = estack.Pop()

                if not isinstance(item, Array):
                    return self.VM_FAULT_and_report(
                        VMFault.UNPACK_INVALID_TYPE, item)

                items = item.GetArray()
                items.reverse()

                [estack.PushT(i) for i in items]

                estack.PushT(len(items))

            elif opcode == PICKITEM:

                key = estack.Pop()

                if isinstance(key, CollectionMixin):
                    # key must be an array index or dictionary key, but not a collection
                    return self.VM_FAULT_and_report(VMFault.KEY_IS_COLLECTION,
                                                    key)

                collection = estack.Pop()

                if isinstance(collection, Array):
                    index = key.GetBigInteger()
                    if index < 0 or index >= collection.Count:
                        return self.VM_FAULT_and_report(
                            VMFault.PICKITEM_INVALID_INDEX, index,
                            collection.Count)

                    items = collection.GetArray()
                    to_pick = items[index]
                    estack.PushT(to_pick)

                elif isinstance(collection, Map):
                    success, value = collection.TryGetValue(key)

                    if success:
                        estack.PushT(value)
                    else:
                        return self.VM_FAULT_and_report(
                            VMFault.DICT_KEY_NOT_FOUND, key, collection.Keys)

                else:
                    return self.VM_FAULT_and_report(
                        VMFault.PICKITEM_INVALID_TYPE, key, collection)

            elif opcode == SETITEM:
                value = estack.Pop()

                if isinstance(value, Struct):
                    value = value.Clone()

                key = estack.Pop()

                if isinstance(key, CollectionMixin):
                    return self.VM_FAULT_and_report(VMFault.KEY_IS_COLLECTION)

                collection = estack.Pop()

                if isinstance(collection, Array):

                    index = key.GetBigInteger()

                    if index < 0 or index >= collection.Count:
                        return self.VM_FAULT_and_report(
                            VMFault.SETITEM_INVALID_INDEX)

                    items = collection.GetArray()
                    items[index] = value

                elif isinstance(collection, Map):

                    collection.SetItem(key, value)

                else:

                    return self.VM_FAULT_and_report(
                        VMFault.SETITEM_INVALID_TYPE, key, collection)

            elif opcode == NEWARRAY:

                count = estack.Pop().GetBigInteger()
                items = [Boolean(False) for i in range(0, count)]
                estack.PushT(Array(items))

            elif opcode == NEWSTRUCT:

                count = estack.Pop().GetBigInteger()

                items = [Boolean(False) for i in range(0, count)]

                estack.PushT(Struct(items))

            elif opcode == NEWMAP:
                estack.PushT(Map())

            elif opcode == APPEND:
                newItem = estack.Pop()

                if isinstance(newItem, Struct):
                    newItem = newItem.Clone()

                arrItem = estack.Pop()

                if not isinstance(arrItem, Array):
                    return self.VM_FAULT_and_report(
                        VMFault.APPEND_INVALID_TYPE, arrItem)

                arr = arrItem.GetArray()
                arr.append(newItem)

            elif opcode == REVERSE:

                arrItem = estack.Pop()
                if not isinstance(arrItem, Array):
                    return self.VM_FAULT_and_report(
                        VMFault.REVERSE_INVALID_TYPE, arrItem)

                arrItem.Reverse()

            elif opcode == REMOVE:

                key = estack.Pop()

                if isinstance(key, CollectionMixin):
                    return self.VM_FAULT_and_report(VMFault.UNKNOWN1)

                collection = estack.Pop()

                if isinstance(collection, Array):

                    index = key.GetBigInteger()

                    if index < 0 or index >= collection.Count:
                        return self.VM_FAULT_and_report(
                            VMFault.REMOVE_INVALID_INDEX, index,
                            collection.Count)

                    collection.RemoveAt(index)

                elif isinstance(collection, Map):

                    collection.Remove(key)

                else:

                    return self.VM_FAULT_and_report(
                        VMFault.REMOVE_INVALID_TYPE, key, collection)

            elif opcode == HASKEY:

                key = estack.Pop()

                if isinstance(key, CollectionMixin):
                    return self.VM_FAULT_and_report(VMFault.DICT_KEY_ERROR)

                collection = estack.Pop()

                if isinstance(collection, Array):

                    index = key.GetBigInteger()

                    if index < 0:
                        return self.VM_FAULT_and_report(VMFault.DICT_KEY_ERROR)

                    estack.PushT(index < collection.Count)

                elif isinstance(collection, Map):

                    estack.PushT(collection.ContainsKey(key))

                else:

                    return self.VM_FAULT_and_report(VMFault.DICT_KEY_ERROR)

            elif opcode == KEYS:

                collection = estack.Pop()

                if isinstance(collection, Map):

                    estack.PushT(Array(collection.Keys))
                else:
                    return self.VM_FAULT_and_report(VMFault.DICT_KEY_ERROR)

            elif opcode == VALUES:

                collection = estack.Pop()
                values = []

                if isinstance(collection, Map):
                    values = collection.Values

                elif isinstance(collection, Array):
                    values = collection

                else:
                    return self.VM_FAULT_and_report(VMFault.DICT_KEY_ERROR)

                newArray = Array()
                for item in values:
                    if isinstance(item, Struct):
                        newArray.Add(item.Clone())
                    else:
                        newArray.Add(item)

                estack.PushT(newArray)

            elif opcode == THROW:
                return self.VM_FAULT_and_report(VMFault.THROW)

            elif opcode == THROWIFNOT:
                if not estack.Pop().GetBoolean():
                    return self.VM_FAULT_and_report(VMFault.THROWIFNOT)

            else:
                return self.VM_FAULT_and_report(VMFault.UNKNOWN_OPCODE, opcode)

        if self._VMState & VMState.FAULT == 0 and self.InvocationStack.Count > 0:
            if len(self.CurrentContext.Breakpoints):
                if self.CurrentContext.InstructionPointer in self.CurrentContext.Breakpoints:
                    self._vm_debugger = VMDebugger(self)
                    self._vm_debugger.start()

    def LoadScript(self, script, push_only=False):

        context = ExecutionContext(self, script, push_only)

        if self._debug_map and context.ScriptHash(
        ) == self._debug_map['script_hash']:
            context.Breakpoints = set(self._debug_map['breakpoints'])

        self._InvocationStack.PushT(context)

        self._ExecutedScriptHashes.append(context.ScriptHash())

    def RemoveBreakPoint(self, position):

        if self._InvocationStack.Count == 0:
            return False
        return self.CurrentContext.Breakpoints.remove(position)

    def StepInto(self):
        if self._InvocationStack.Count == 0:
            self._VMState |= VMState.HALT

        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            logger.info("stopping because vm state is %s " % self._VMState)
            return

        op = None

        if self.CurrentContext.InstructionPointer >= len(
                self.CurrentContext.Script):
            op = RET
        else:
            op = self.CurrentContext.OpReader.ReadByte(do_ord=False)

        self.ops_processed += 1

        try:
            if self._is_write_log:
                self.write_log("{} {}".format(self.ops_processed, ToName(op)))
            self.ExecuteOp(op, self.CurrentContext)
        except Exception as e:
            error_msg = "COULD NOT EXECUTE OP (%s): %s %s %s" % (
                self.ops_processed, e, op, ToName(op))
            self.write_log(error_msg)

            if self._exit_on_error:
                self._VMState |= VMState.FAULT
            else:
                logger.error(error_msg)
                logger.exception(e)

    def StepOut(self):
        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:
            self.StepInto()

    def StepOver(self):
        if self._VMState & VMState.HALT > 0 or self._VMState & VMState.FAULT > 0:
            return

        self._VMState &= ~VMState.BREAK
        count = self._InvocationStack.Count

        while self._VMState & VMState.HALT == 0 and \
                self._VMState & VMState.FAULT == 0 and \
                self._VMState & VMState.BREAK == 0 and \
                self._InvocationStack.Count > count:
            self.StepInto()

    def VM_FAULT_and_report(self, id, *args):
        self._VMState |= VMState.FAULT

        if settings.log_level != LOGGING_LEVEL_DEBUG:
            return

        if id == VMFault.INVALID_JUMP:
            error_msg = "Attemping to JMP/JMPIF/JMPIFNOT to an invalid location."

        elif id == VMFault.INVALID_CONTRACT:
            script_hash = args[0]
            error_msg = "Trying to call an unknown contract with script_hash {}\nMake sure the contract exists on the blockchain".format(
                script_hash)

        elif id == VMFault.CHECKMULTISIG_INVALID_PUBLICKEY_COUNT:
            error_msg = "CHECKMULTISIG - provided public key count is less than 1."

        elif id == VMFault.CHECKMULTISIG_SIGNATURE_ERROR:
            if args[0] < 1:
                error_msg = "CHECKMULTISIG - Minimum required signature count cannot be less than 1."
            else:  # m > n
                m = args[0]
                n = args[1]
                error_msg = "CHECKMULTISIG - Insufficient signatures provided ({}). Minimum required is {}".format(
                    m, n)

        elif id == VMFault.UNPACK_INVALID_TYPE:
            item = args[0]
            error_msg = "Failed to UNPACK item. Item is not an array but of type: {}".format(
                type(item))

        elif id == VMFault.PICKITEM_INVALID_TYPE:
            index = args[0]
            item = args[1]
            error_msg = "Cannot access item at index {}. Item is not an array or dict but of type: {}".format(
                index, type(item))

        elif id == VMFault.PICKITEM_NEGATIVE_INDEX:
            error_msg = "Attempting to access an array using a negative index"

        elif id == VMFault.PICKITEM_INVALID_INDEX:
            index = args[0]
            length = args[1]
            error_msg = "Array index is less than zero or {} exceeds list length {}".format(
                index, length)

        elif id == VMFault.APPEND_INVALID_TYPE:
            item = args[0]
            error_msg = "Cannot append to item. Item is not an array but of type: {}".format(
                type(item))

        elif id == VMFault.REVERSE_INVALID_TYPE:
            item = args[0]
            error_msg = "Cannot REVERSE item. Item is not an array but of type: {}".format(
                type(item))

        elif id == VMFault.REMOVE_INVALID_TYPE:
            item = args[0]
            index = args[1]
            error_msg = "Cannot REMOVE item at index {}. Item is not an array but of type: {}".format(
                index, type(item))

        elif id == VMFault.REMOVE_INVALID_INDEX:
            index = args[0]
            length = args[1]

            if index < 0:
                error_msg = "Cannot REMOVE item at index {}. Index < 0".format(
                    index)

            else:  # index >= len(items):
                error_msg = "Cannot REMOVE item at index {}. Index exceeds array length {}".format(
                    index, length)

        elif id == VMFault.POP_ITEM_NOT_ARRAY:
            error_msg = "Items(s) not array: %s" % [item for item in args]

        elif id == VMFault.UNKNOWN_OPCODE:
            opcode = args[0]
            error_msg = "Unknown opcode found: {}".format(opcode)

        else:
            error_msg = id

        if id in [VMFault.THROW, VMFault.THROWIFNOT]:
            logger.debug("({}) {}".format(self.ops_processed, id))
        else:
            logger.error("({}) {}".format(self.ops_processed, error_msg))

        return