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 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 LoadScript(self, script, rvcount=-1) -> ExecutionContext: context = ExecutionContext(self, 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']: self._breakpoints[script_hash] = set(self._debug_map['breakpoints']) return context
def test_get_item_count(self): econtext1 = ExecutionContext(engine=self.engine) # 4 items in context 1 map = Map({'a': 1, 'b': 2, 'c': 3}) my_int = Integer(BigInteger(1)) econtext1.EvaluationStack.PushT(map) econtext1.EvaluationStack.PushT(my_int) # 3 items in context 2 econtext2 = ExecutionContext(engine=self.engine) my_array = Array([my_int, my_int]) econtext2.EvaluationStack.PushT(my_array) econtext2.AltStack.PushT(my_int) self.engine.InvocationStack.PushT(econtext1) self.engine.InvocationStack.PushT(econtext2) stack_item_list = [] for execution_context in self.engine.InvocationStack.Items: # type: ExecutionContext stack_item_list += execution_context.EvaluationStack.Items + execution_context.AltStack.Items self.assertEqual(7, self.engine.GetItemCount(stack_item_list))
def LoadScript(self, script, push_only=False): context = ExecutionContext(self, script, push_only) self._InvocationStack.PushT(context) self._ExecutedScriptHashes.append(context.ScriptHash())
def LoadScript(self, script, push_only=False): context = ExecutionContext(self, script, push_only) self._InvocationStack.PushT(context)
def setUp(self): self.engine = ExecutionEngine() self.econtext = ExecutionContext(engine=self.engine)
def setUp(self): self.engine = ExecutionEngine() self.econtext = ExecutionContext() self.state_reader = StateReader()
def setUp(self): self.engine = ExecutionEngine() self.econtext = ExecutionContext(Script(self.engine.Crypto, b''), 0) self.engine.InvocationStack.PushT(self.econtext) snapshot = GetBlockchain()._db.createSnapshot() self.state_reader = StateReader(TriggerType.Application, snapshot)
def ExecuteOp(self, opcode, context: ExecutionContext): estack = context._EvaluationStack istack = self._InvocationStack astack = context._AltStack 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: 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: context_call = self.LoadScript(context.Script) context.EvaluationStack.CopyTo(context_call.EvaluationStack) context_call.InstructionPointer = context.InstructionPointer context.EvaluationStack.Clear() context.InstructionPointer += 2 self.ExecuteOp(JMP, context_call) 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) 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 = estack.Pop().GetByteArray() script = self._Table.GetScript( UInt160(data=script_hash).ToBytes()) if script is None: return self.VM_FAULT_and_report(VMFault.INVALID_CONTRACT, script_hash) context_new = self.LoadScript(script) estack.CopyTo(context_new.EvaluationStack) if opcode == TAILCALL: istack.Remove(1).Dispose() else: estack.Clear() 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: estack.Remove(1) elif opcode == OVER: estack.PushT(estack.Peek(1)) 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: estack.PushT(estack.Remove(2)) elif opcode == SWAP: estack.PushT(estack.Remove(1)) elif opcode == TUCK: estack.Insert(2, estack.Peek()) 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() 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.error("Could not checksig: %s " % e) 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.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) # stack isolation elif opcode == CALL_I: rvcount = context.OpReader.ReadByte() pcount = context.OpReader.ReadByte() if estack.Count < pcount: return self.VM_FAULT_and_report( VMFault.UNKNOWN_STACKISOLATION) context_call = self.LoadScript(context.Script, rvcount) estack.CopyTo(context_call.EvaluationStack, pcount) context_call.InstructionPointer = context.InstructionPointer for i in range(0, pcount, 1): estack.Pop() context.InstructionPointer += 2 self.ExecuteOp(JMP, context_call) 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 = context.OpReader.ReadByte() pcount = context.OpReader.ReadByte() 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) if opcode in [CALL_ED, CALL_EDT]: script_hash = estack.Pop().GetByteArray() else: script_hash = context.OpReader.ReadBytes(20) 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) context_new = self.LoadScript(script, rvcount) estack.CopyTo(context_new.EvaluationStack, pcount) if opcode in [CALL_ET, CALL_EDT]: istack.Remove(1).Dispose() 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) else: return self.VM_FAULT_and_report(VMFault.UNKNOWN_OPCODE, opcode) if self._VMState & VMState.FAULT == 0 and self.InvocationStack.Count > 0: script_hash = self.CurrentContext.ScriptHash() bps = self._breakpoints.get(self.CurrentContext.ScriptHash(), None) if bps is not None: if self.CurrentContext.InstructionPointer in bps: self._vm_debugger = VMDebugger(self) self._vm_debugger.start()
def setUp(self): self.engine = ExecutionEngine() self.econtext = ExecutionContext(Script(self.engine.Crypto, b''), 0) self.engine.InvocationStack.PushT(self.econtext)