def __init__(self, engine=None, script=None, rvcount=0):
     self.Script = script
     self.__mstream = StreamManager.GetStream(self.Script)
     self.__OpReader = BinaryReader(self.__mstream)
     self._EvaluationStack = RandomAccessStack(name='Evaluation')
     self._AltStack = RandomAccessStack(name='Alt')
     self._RVCount = rvcount
     self.crypto = engine.Crypto
예제 #2
0
    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')
예제 #3
0
 def __init__(self, script: 'Script', rvcount: int):
     self.instructions = {}
     self._EvaluationStack = RandomAccessStack(name='Evaluation')
     self._AltStack = RandomAccessStack(name='Alt')
     self.InstructionPointer = 0
     self.Script = script
     self._RVCount = rvcount
     self._script_hash = None
     self.ins = None
예제 #4
0
 def __init__(self, container=None, crypto=None, table=None, service=None, exit_on_error=False):
     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
예제 #5
0
 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._ResultStack = RandomAccessStack(name='Result')
     self._ExecutedScriptHashes = []
     self.ops_processed = 0
     self._debug_map = None
     self._is_write_log = settings.log_vm_instructions
     self._breakpoints = dict()
예제 #6
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()
예제 #7
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()
예제 #8
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
예제 #9
0
class ExecutionEngine:
    log_file_name = 'vm_instructions.log'
    # file descriptor
    log_file = None
    _vm_debugger = None

    MaxSizeForBigInteger = 32
    max_shl_shr = 256
    min_shl_shr = -256
    maxItemSize = 1024 * 1024
    maxArraySize = 1024
    maxStackSize = 2048
    maxInvocationStackSize = 1024

    def __init__(self,
                 container=None,
                 crypto=None,
                 table=None,
                 service=None,
                 exit_on_error=True):
        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._ResultStack = RandomAccessStack(name='Result')
        self._ExecutedScriptHashes = []
        self.ops_processed = 0
        self._debug_map = None
        self._is_write_log = settings.log_vm_instructions
        self._is_stackitem_count_strict = True
        self._stackitem_count = 0
        self._EntryScriptHash = None

    def CheckArraySize(self, length: int) -> bool:
        return length <= self.maxArraySize

    def CheckMaxItemSize(self, length: int) -> bool:
        return length >= 0 and length <= self.maxItemSize

    def CheckMaxInvocationStack(self) -> bool:
        return self.InvocationStack.Count < self.maxInvocationStackSize

    def CheckBigInteger(self, value: 'BigInteger') -> bool:
        return len(value.ToByteArray()) <= self.MaxSizeForBigInteger

    def CheckShift(self, shift: int) -> bool:
        return shift <= self.max_shl_shr and shift >= self.min_shl_shr

    def CheckStackSize(self, strict: bool, count: int = 1) -> bool:
        self._is_stackitem_count_strict &= strict
        self._stackitem_count += count

        # the C# implementation expects to overflow a signed int into negative when supplying a count of int.MaxValue so we check for exceeding int.MaxValue
        if self._stackitem_count < 0 or self._stackitem_count > int_MaxValue:
            self._stackitem_count = int_MaxValue

        if self._stackitem_count <= self.maxStackSize:
            return True

        if self._is_stackitem_count_strict:
            return False

        stack_item_list = []
        for execution_context in self.InvocationStack.Items:  # type: ExecutionContext
            stack_item_list += execution_context.EvaluationStack.Items + execution_context.AltStack.Items

        self._stackitem_count = self.GetItemCount(stack_item_list)
        if self._stackitem_count > self.maxStackSize:
            return False

        self._is_stackitem_count_strict = True
        return True

    def GetItemCount(self, items_list):  # list of StackItems
        count = 0
        items = deque(items_list)
        counted = []
        while items:
            stackitem = items.pop()

            if stackitem.IsTypeMap:
                if stackitem in counted:
                    continue

                counted.append(stackitem)

                items.extend(stackitem.Values)
                continue

            if stackitem.IsTypeArray:
                if stackitem in counted:
                    continue

                counted.append(stackitem)

                items.extend(stackitem.GetArray())
                continue
            count += 1

        return count

    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 ResultStack(self):
        return self._ResultStack

    @property
    def CurrentContext(self) -> ExecutionContext:
        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 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):
        self.InvocationStack.Clear()

    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.ExecuteNext()

        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()

        return not self._VMState & VMState.FAULT > 0

    def ExecuteInstruction(self):
        context = self.CurrentContext
        instruction = context.CurrentInstruction
        opcode = instruction.OpCode
        estack = context._EvaluationStack
        istack = self._InvocationStack
        astack = context._AltStack

        if opcode >= PUSHBYTES1 and opcode <= PUSHDATA4:
            if not self.CheckMaxItemSize(len(instruction.Operand)):
                return False
            estack.PushT(instruction.Operand)

            if not self.CheckStackSize(True):
                return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)
        else:

            # push values
            if opcode in [
                    PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7,
                    PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14,
                    PUSH15, PUSH16
            ]:

                topush = int.from_bytes(opcode, 'little') - int.from_bytes(
                    PUSH1, 'little') + 1
                estack.PushT(topush)

                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == PUSH0:
                estack.PushT(bytearray(0))
                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            # control
            elif opcode == NOP:
                pass
            elif opcode in [JMP, JMPIF, JMPIFNOT]:
                offset = context.InstructionPointer + instruction.TokenI16

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

                fValue = True
                if opcode > JMP:
                    self.CheckStackSize(False, -1)
                    fValue = estack.Pop().GetBoolean()
                    if opcode == JMPIFNOT:
                        fValue = not fValue
                if fValue:
                    context.InstructionPointer = offset
                    context.ins = context.GetInstruction(
                        context.InstructionPointer)
                else:
                    context.InstructionPointer += 3
                    context.ins = context.GetInstruction(
                        context.InstructionPointer)
                return True

            elif opcode == CALL:
                if not self.CheckMaxInvocationStack():
                    return self.VM_FAULT_and_report(
                        VMFault.CALL_EXCEED_MAX_INVOCATIONSTACK_SIZE)

                context_call = self._LoadScriptInternal(context.Script)
                context_call.InstructionPointer = context.InstructionPointer + instruction.TokenI16
                if context_call.InstructionPointer < 0 or context_call.InstructionPointer > context_call.Script.Length:
                    return False
                context.EvaluationStack.CopyTo(context_call.EvaluationStack)
                context.EvaluationStack.Clear()

            elif opcode == RET:
                context_pop: ExecutionContext = istack.Pop()
                rvcount = context_pop._RVCount

                if rvcount == -1:
                    rvcount = context_pop.EvaluationStack.Count

                if rvcount > 0:
                    if context_pop.EvaluationStack.Count < rvcount:
                        return self.VM_FAULT_and_report(VMFault.UNKNOWN1)

                    if istack.Count == 0:
                        stack_eval = self._ResultStack
                    else:
                        stack_eval = self.CurrentContext.EvaluationStack
                    context_pop.EvaluationStack.CopyTo(stack_eval, rvcount)

                if context_pop._RVCount == -1 and istack.Count > 0:
                    context_pop.AltStack.CopyTo(self.CurrentContext.AltStack)

                self.CheckStackSize(False, 0)

                if istack.Count == 0:
                    self._VMState = VMState.HALT
                return True

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

                if opcode == APPCALL and not self.CheckMaxInvocationStack():
                    return self.VM_FAULT_and_report(
                        VMFault.APPCALL_EXCEED_MAX_INVOCATIONSTACK_SIZE)

                script_hash = instruction.Operand

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

                if not is_normal_call:
                    script_hash = estack.Pop().GetByteArray()

                context_new = self._LoadScriptByHash(script_hash)
                if context_new is None:
                    return self.VM_FAULT_and_report(VMFault.INVALID_CONTRACT,
                                                    script_hash)

                estack.CopyTo(context_new.EvaluationStack)

                if opcode == TAILCALL:
                    istack.Remove(1)
                else:
                    estack.Clear()

                self.CheckStackSize(False, 0)

            elif opcode == SYSCALL:
                if len(instruction.Operand) > 252:
                    return False

                if not self._Service.Invoke(instruction.Operand, self):
                    return self.VM_FAULT_and_report(VMFault.SYSCALL_ERROR,
                                                    instruction.Operand)

                if not self.CheckStackSize(False, int_MaxValue):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

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

                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            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)
                self.CheckStackSize(False, -2)

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

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

                self.CheckStackSize(True, -1)

                # 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)
                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == DROP:
                estack.Pop()
                self.CheckStackSize(False, -1)

            elif opcode == DUP:
                estack.PushT(estack.Peek())
                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == NIP:
                estack.Remove(1)
                self.CheckStackSize(False, -1)

            elif opcode == OVER:
                estack.PushT(estack.Peek(1))
                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            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)
                self.CheckStackSize(True, -1)

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

            elif opcode == ROT:
                estack.PushT(estack.Remove(2))

            elif opcode == SWAP:
                estack.PushT(estack.Remove(1))

            elif opcode == TUCK:
                estack.Insert(2, estack.Peek())
                if not self.CheckStackSize(True):
                    return self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == CAT:

                x2 = estack.Pop().GetByteArray()
                x1 = estack.Pop().GetByteArray()
                if not self.CheckMaxItemSize(len(x1) + len(x2)):
                    return self.VM_FAULT_and_report(
                        VMFault.CAT_EXCEED_MAXITEMSIZE)
                estack.PushT(x1 + x2)
                self.CheckStackSize(True, -1)

            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])
                self.CheckStackSize(True, -2)

            elif opcode == LEFT:

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

                x = estack.Pop().GetByteArray()

                if count >= len(x):
                    estack.PushT(x)
                else:
                    estack.PushT(x[:count])
                self.CheckStackSize(True, -1)

            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 count > len(x):
                    return self.VM_FAULT_and_report(VMFault.RIGHT_UNKNOWN)

                if count == len(x):
                    estack.PushT(x)
                else:
                    offset = len(x) - count
                    estack.PushT(x[offset:offset + count])
                self.CheckStackSize(True, -1)

            elif opcode == SIZE:

                x = estack.Pop()
                estack.PushT(x.GetByteLength())

            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)
                self.CheckStackSize(True, -1)

            elif opcode == OR:

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

                estack.PushT(x1 | x2)
                self.CheckStackSize(True, -1)

            elif opcode == XOR:

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

                estack.PushT(x1 ^ x2)
                self.CheckStackSize(True, -1)

            elif opcode == EQUAL:
                x2 = estack.Pop()
                x1 = estack.Pop()
                estack.PushT(x1.Equals(x2))
                self.CheckStackSize(False, -1)

            # numeric
            elif opcode == INC:

                x = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x) or not self.CheckBigInteger(x +
                                                                           1):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)
                estack.PushT(x + 1)

            elif opcode == DEC:

                x = estack.Pop().GetBigInteger()  # type: BigInteger
                if not self.CheckBigInteger(x) or (
                        x.Sign <= 0 and not self.CheckBigInteger(x - 1)):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                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().GetBoolean()
                estack.PushT(not x)
                self.CheckStackSize(False, 0)

            elif opcode == NZ:

                x = estack.Pop().GetBigInteger()

                estack.PushT(x is not 0)

            elif opcode == ADD:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x1) or not self.CheckBigInteger(
                        x2) or not self.CheckBigInteger(x1 + x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x1 + x2)
                self.CheckStackSize(True, -1)

            elif opcode == SUB:

                x2 = estack.Pop().GetBigInteger()
                x1 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x1) or not self.CheckBigInteger(
                        x2) or not self.CheckBigInteger(x1 - x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x1 - x2)
                self.CheckStackSize(True, -1)

            elif opcode == MUL:

                x2 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                x1 = estack.Pop().GetBigInteger()  # type: BigInteger
                if not self.CheckBigInteger(x1):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                result = x1 * x2
                if not self.CheckBigInteger(result):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(result)
                self.CheckStackSize(True, -1)

            elif opcode == DIV:

                x2 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                x1 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x1) or not self.CheckBigInteger(
                        x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x1 / x2)
                self.CheckStackSize(True, -1)

            elif opcode == MOD:

                x2 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x2):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                x1 = estack.Pop().GetBigInteger()
                if not self.CheckBigInteger(x1):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x1 % x2)
                self.CheckStackSize(True, -1)

            elif opcode == SHL:

                shift = estack.Pop().GetBigInteger()
                if not self.CheckShift(shift):
                    return self.VM_FAULT_and_report(VMFault.INVALID_SHIFT)

                x = estack.Pop().GetBigInteger()

                if not self.CheckBigInteger(x):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                x = x << shift

                if not self.CheckBigInteger(x):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x)
                self.CheckStackSize(True, -1)

            elif opcode == SHR:

                shift = estack.Pop().GetBigInteger()
                if not self.CheckShift(shift):
                    return self.VM_FAULT_and_report(VMFault.INVALID_SHIFT)

                x = estack.Pop().GetBigInteger()

                if not self.CheckBigInteger(x):
                    return self.VM_FAULT_and_report(
                        VMFault.BIGINTEGER_EXCEED_LIMIT)

                estack.PushT(x >> shift)
                self.CheckStackSize(True, -1)

            elif opcode == BOOLAND:

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

                estack.PushT(x1 and x2)
                self.CheckStackSize(False, -1)

            elif opcode == BOOLOR:

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

                estack.PushT(x1 or x2)
                self.CheckStackSize(False, -1)

            elif opcode == NUMEQUAL:

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

                estack.PushT(x2 == x1)
                self.CheckStackSize(True, -1)

            elif opcode == NUMNOTEQUAL:

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

                estack.PushT(x1 != x2)
                self.CheckStackSize(True, -1)

            elif opcode == LT:

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

                estack.PushT(x1 < x2)
                self.CheckStackSize(True, -1)

            elif opcode == GT:

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

                estack.PushT(x1 > x2)
                self.CheckStackSize(True, -1)

            elif opcode == LTE:

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

                estack.PushT(x1 <= x2)
                self.CheckStackSize(True, -1)

            elif opcode == GTE:

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

                estack.PushT(x1 >= x2)
                self.CheckStackSize(True, -1)

            elif opcode == MIN:

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

                estack.PushT(min(x1, x2))
                self.CheckStackSize(True, -1)

            elif opcode == MAX:

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

                estack.PushT(max(x1, x2))
                self.CheckStackSize(True, -1)

            elif opcode == WITHIN:

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

                estack.PushT(a <= x and x < b)
                self.CheckStackSize(True, -2)

            # 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()
                container = self.ScriptContainer
                if not container:
                    logger.debug("Cannot check signature without container")
                    estack.PushT(False)
                    return
                try:
                    res = self.Crypto.VerifySignature(container.GetMessage(),
                                                      sig, pubkey)
                    estack.PushT(res)
                except Exception as e:
                    estack.PushT(False)
                    logger.debug("Could not checksig: %s " % e)
                self.CheckStackSize(True, -1)

            elif opcode == VERIFY:
                pubkey = estack.Pop().GetByteArray()
                sig = estack.Pop().GetByteArray()
                message = estack.Pop().GetByteArray()
                try:
                    res = self.Crypto.VerifySignature(message,
                                                      sig,
                                                      pubkey,
                                                      unhex=False)
                    estack.PushT(res)
                except Exception as e:
                    estack.PushT(False)
                    logger.debug("Could not verify: %s " % e)
                self.CheckStackSize(True, -2)

            elif opcode == CHECKMULTISIG:
                item = estack.Pop()
                pubkeys = []

                if isinstance(item, Array):

                    for p in item.GetArray():
                        pubkeys.append(p.GetByteArray())
                    n = len(pubkeys)
                    if n == 0:
                        return self.VM_FAULT_and_report(
                            VMFault.CHECKMULTISIG_INVALID_PUBLICKEY_COUNT)

                    self.CheckStackSize(False, -1)
                else:
                    n = item.GetBigInteger()

                    if n < 1 or n > estack.Count:
                        return self.VM_FAULT_and_report(
                            VMFault.CHECKMULTISIG_INVALID_PUBLICKEY_COUNT)

                    for i in range(0, n):
                        pubkeys.append(estack.Pop().GetByteArray())
                    self.CheckStackSize(True, -n - 1)

                item = estack.Pop()
                sigs = []

                if isinstance(item, Array):
                    for s in item.GetArray():
                        sigs.append(s.GetByteArray())
                    m = len(sigs)

                    if m == 0 or m > n:
                        return self.VM_FAULT_and_report(
                            VMFault.CHECKMULTISIG_SIGNATURE_ERROR, m, n)
                    self.CheckStackSize(False, -1)
                else:
                    m = item.GetBigInteger()

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

                    for i in range(0, m):
                        sigs.append(estack.Pop().GetByteArray())
                    self.CheckStackSize(True, -m - 1)

                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)
                    self.CheckStackSize(False, 0)

                else:
                    estack.PushT(len(item.GetByteArray()))
                    self.CheckStackSize(True, 0)

            elif opcode == PACK:

                size = estack.Pop().GetBigInteger()

                if size < 0 or size > estack.Count or not self.CheckArraySize(
                        size):
                    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))
                if not self.CheckStackSize(False, len(items)):
                    self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            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)

                    if not self.CheckStackSize(False, -1):
                        self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

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

                    if success:
                        estack.PushT(value)

                        if not self.CheckStackSize(False, -1):
                            self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

                    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):
                    if not collection.ContainsKey(
                            key) and not self.CheckArraySize(collection.Count +
                                                             1):
                        return self.VM_FAULT_and_report(
                            VMFault.SETITEM_INVALID_MAP)

                    collection.SetItem(key, value)

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

                if not self.CheckStackSize(False, int_MaxValue):
                    self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode in [NEWARRAY, NEWSTRUCT]:
                item = estack.Pop()
                if isinstance(item, Array):
                    result = None
                    if isinstance(item, Struct):
                        if opcode == NEWSTRUCT:
                            result = item
                    else:
                        if opcode == NEWARRAY:
                            result = item

                    if result is None:
                        result = Array(item) if opcode == NEWARRAY else Struct(
                            item)

                    estack.PushT(result)

                else:
                    count = item.GetBigInteger()
                    if count < 0:
                        return self.VM_FAULT_and_report(
                            VMFault.NEWARRAY_NEGATIVE_COUNT)

                    if not self.CheckArraySize(count):
                        return self.VM_FAULT_and_report(
                            VMFault.NEWARRAY_EXCEED_ARRAYLIMIT)

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

                    result = Array(items) if opcode == NEWARRAY else Struct(
                        items)

                    estack.PushT(result)

                    if not self.CheckStackSize(True, count):
                        self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == NEWMAP:
                estack.PushT(Map())
                if not self.CheckStackSize(True):
                    self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            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()
                if not self.CheckArraySize(len(arr) + 1):
                    return self.VM_FAULT_and_report(
                        VMFault.APPEND_EXCEED_ARRAYLIMIT)
                arr.append(newItem)

                if not self.CheckStackSize(False, int_MaxValue):
                    self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            elif opcode == REVERSE:

                arrItem = estack.Pop()
                self.CheckStackSize(False, -1)

                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()
                self.CheckStackSize(False, -2)

                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)
                self.CheckStackSize(False, -1)

            elif opcode == KEYS:

                collection = estack.Pop()

                if isinstance(collection, Map):

                    estack.PushT(Array(collection.Keys))
                    if not self.CheckStackSize(False, collection.Count):
                        self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)
                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)
                if not self.CheckStackSize(False, int_MaxValue):
                    self.VM_FAULT_and_report(VMFault.INVALID_STACKSIZE)

            # stack isolation
            elif opcode == CALL_I:
                if not self.CheckMaxInvocationStack():
                    return self.VM_FAULT_and_report(
                        VMFault.CALL__I_EXCEED_MAX_INVOCATIONSTACK_SIZE)
                rvcount = instruction.Operand[0]
                pcount = instruction.Operand[1]

                if estack.Count < pcount:
                    return self.VM_FAULT_and_report(
                        VMFault.UNKNOWN_STACKISOLATION)

                context_call = self._LoadScriptInternal(
                    context.Script, rvcount)
                context_call.InstructionPointer = context.InstructionPointer + instruction.TokenI16_1 + 2

                if context_call.InstructionPointer < 0 or context_call.InstructionPointer > context_call.Script.Length:
                    return False

                estack.CopyTo(context_call.EvaluationStack, pcount)

                for i in range(0, pcount, 1):
                    estack.Pop()

            elif opcode in [CALL_E, CALL_ED, CALL_ET, CALL_EDT]:
                if self._Table is None:
                    return self.VM_FAULT_and_report(
                        VMFault.UNKNOWN_STACKISOLATION2)

                rvcount = instruction.Operand[0]
                pcount = instruction.Operand[1]

                if estack.Count < pcount:
                    return self.VM_FAULT_and_report(
                        VMFault.UNKNOWN_STACKISOLATION)

                if opcode in [CALL_ET, CALL_EDT]:
                    if context._RVCount != rvcount:
                        return self.VM_FAULT_and_report(
                            VMFault.UNKNOWN_STACKISOLATION3)
                else:
                    if not self.CheckMaxInvocationStack():
                        return self.VM_FAULT_and_report(
                            VMFault.UNKNOWN_EXCEED_MAX_INVOCATIONSTACK_SIZE)

                if opcode in [CALL_ED, CALL_EDT]:
                    script_hash = estack.Pop().GetByteArray()
                    self.CheckStackSize(True, -1)
                else:
                    script_hash = instruction.ReadBytes(2, 20)

                context_new = self._LoadScriptByHash(script_hash, rvcount)
                if context_new is None:
                    return self.VM_FAULT_and_report(VMFault.INVALID_CONTRACT,
                                                    script_hash)

                estack.CopyTo(context_new.EvaluationStack, pcount)

                if opcode in [CALL_ET, CALL_EDT]:
                    istack.Remove(1)
                else:
                    for i in range(0, pcount, 1):
                        estack.Pop()

            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)
                self.CheckStackSize(False, -1)

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

    def LoadScript(self,
                   script: bytearray,
                   rvcount: int = -1) -> ExecutionContext:
        # "raw" bytes
        new_script = Script(self.Crypto, script)

        return self._LoadScriptInternal(new_script, rvcount)

    def _LoadScriptInternal(self, script: Script, rvcount=-1):
        context = ExecutionContext(script, rvcount)
        self._InvocationStack.PushT(context)
        self._ExecutedScriptHashes.append(context.ScriptHash())

        # add break points for current script if available
        script_hash = context.ScriptHash()
        if self._debug_map and script_hash == self._debug_map['script_hash']:
            if self.debugger:
                self.debugger._breakpoints[script_hash] = set(
                    self._debug_map['breakpoints'])

        return context

    def _LoadScriptByHash(self, script_hash: bytearray, rvcount=-1):

        if self._Table is None:
            return None
        script = self._Table.GetScript(UInt160(data=script_hash).ToBytes())
        if script is None:
            return None
        return self._LoadScriptInternal(Script.FromHash(script_hash, script),
                                        rvcount)

    def PreExecuteInstruction(self):
        # allow overriding
        return True

    def PostExecuteInstruction(self):
        # allow overriding
        return True

    def ExecuteNext(self):
        if self._InvocationStack.Count == 0:
            self._VMState = VMState.HALT
        else:
            self.ops_processed += 1

            try:
                instruction = self.CurrentContext.CurrentInstruction

                if self._is_write_log:
                    if instruction.InstructionName == "SYSCALL":
                        if len(instruction.Operand) > 4:
                            call = instruction.Operand.decode('ascii')
                            self.write_log("{} {} {} {}".format(
                                self.ops_processed,
                                instruction.InstructionName, call,
                                self.CurrentContext.InstructionPointer))
                        else:
                            self.write_log("{} {} {} {}".format(
                                self.ops_processed,
                                instruction.InstructionName,
                                instruction.TokenU32,
                                self.CurrentContext.InstructionPointer))
                else:
                    self.write_log("{} {} {}".format(
                        self.ops_processed, instruction.InstructionName,
                        self.CurrentContext.InstructionPointer))

                if not self.PreExecuteInstruction():
                    self._VMState = VMState.FAULT
                if not self.ExecuteInstruction():
                    self._VMState = VMState.FAULT
                if not self.PostExecuteInstruction():
                    self._VMState = VMState.FAULT
            except Exception as e:

                error_msg = f"COULD NOT EXECUTE OP ({self.ops_processed}): {e}"
                # traceback.print_exc()
                self.write_log(error_msg)

                if self._exit_on_error:
                    self._VMState = VMState.FAULT

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

        if not logger.hasHandlers(
        ) or logger.handlers[0].level != LOGGING_LEVEL_DEBUG:
            return False

        # 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 Map 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.debug("({}) {}".format(self.ops_processed, error_msg))

        return False