def test_serialize_map(self): map2 = Map({ StackItem.New(b'a'): StackItem.New(1), StackItem.New(b'b'): StackItem.New(2), StackItem.New(b'c'): StackItem.New(3), }) self.engine.EvaluationStack.PushT(map2) self.state_reader.Runtime_Serialize(self.engine) self.state_reader.Runtime_Deserialize(self.engine) deserialized = self.engine.EvaluationStack.Pop() self.assertEqual(deserialized, map2) map3 = Map({ StackItem.New(b'j'): StackItem.New(8), StackItem.New(b'k'): StackItem.New(2222), }) map2.SetItem(StackItem.New(b'mymap'), map3) self.engine.EvaluationStack.PushT(map2) self.state_reader.Runtime_Serialize(self.engine) self.state_reader.Runtime_Deserialize(self.engine) deserialized = self.engine.EvaluationStack.Pop() self.assertEqual(deserialized, map2)
def test_op_map7(self): with self.assertLogHandler('vm', logging.DEBUG) as log_context: # pick item with key is collection causes error self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4)})) self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4)})) self.engine.ExecuteOp(OpCode.PICKITEM, self.econtext) self.assertEqual(self.engine.State, VMState.FAULT | VMState.BREAK) self.assertTrue(len(log_context.output) > 0) self.assertTrue('VMFault.KEY_IS_COLLECTION' in log_context.output[0])
def test_op_map7(self, mocked_logger): # pick item with key is collection causes error self.econtext.EvaluationStack.PushT( Map(dict={StackItem.New('a'): StackItem.New(4)})) self.econtext.EvaluationStack.PushT( Map(dict={StackItem.New('a'): StackItem.New(4)})) self.engine.ExecuteOp(OpCode.PICKITEM, self.econtext) self.assertEqual(self.engine.State, VMState.FAULT | VMState.BREAK) mocked_logger.assert_called_with(StringIn('VMFault.KEY_IS_COLLECTION'))
def test_op_map13(self): self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4), StackItem.New('b'): StackItem.New(5)})) self.econtext.EvaluationStack.PushT(StackItem.New('c')) self.econtext.Script._value = OpCode.HASKEY self.engine.ExecuteInstruction() self.assertEqual(self.econtext.EvaluationStack.Items[0].GetBoolean(), False)
def test_op_map11(self): self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4), StackItem.New('b'): StackItem.New(5)})) self.engine.ExecuteOp(OpCode.VALUES, self.econtext) self.assertIsInstance(self.econtext.EvaluationStack.Items[0], Array) items = self.econtext.EvaluationStack.Items[0].GetArray() self.assertEqual(items, [StackItem.New(4), StackItem.New(5)])
def test_iter_map(self): my_map = Map({ StackItem.New('a'): StackItem.New(1), StackItem.New('b'): StackItem.New(3), StackItem.New('d'): StackItem.New(432) }) self.econtext.EvaluationStack.PushT(my_map) self.engine.InvocationStack.PushT(self.econtext) self.service.Iterator_Create(self.engine) iterable = self.econtext.EvaluationStack.Peek(0).GetInterface() self.assertIsInstance(iterable, MapWrapper) keys = [] values = [] while iterable.Next(): keys.append(iterable.Key()) values.append(iterable.Value()) self.assertEqual( keys, [StackItem.New('a'), StackItem.New('b'), StackItem.New('d')]) self.assertEqual(keys, my_map.Keys) self.assertEqual( values, [StackItem.New(1), StackItem.New(3), StackItem.New(432)]) self.assertEqual(values, my_map.Values)
def test_op_map6(self): # we can pick an item from a dict self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4)})) self.econtext.EvaluationStack.PushT(StackItem.New('a')) self.engine.ExecuteOp(OpCode.PICKITEM, self.econtext) self.assertEqual(len(self.econtext.EvaluationStack.Items), 1) self.assertEqual(self.econtext.EvaluationStack.Items[0].GetBigInteger(), 4)
def test_op_map10(self): # pick item key not found self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4), StackItem.New('b'): StackItem.New(5)})) self.econtext.Script._value = OpCode.KEYS self.engine.ExecuteInstruction() self.assertIsInstance(self.econtext.EvaluationStack.Items[0], Array) items = self.econtext.EvaluationStack.Items[0].GetArray() self.assertEqual(items, [StackItem.New('a'), StackItem.New('b')])
def test_interop_map1(self): map = Map() self.assertEqual(map.Keys, []) self.assertEqual(map.Values, []) map.SetItem(Integer(BigInteger(3)), ByteArray(b'abc')) self.assertEqual(map.Keys, [Integer(BigInteger(3))]) self.assertEqual(map.Values, [ByteArray(b'abc')])
def test_interop_map2(self): map = Map({'a': 1, 'b': 2, 'c': 3}) self.assertEqual(map.Count, 3) self.assertEqual(map.ContainsKey('a'), True) self.assertEqual(map.Contains('a'), False) map.Clear() self.assertEqual(map.GetMap(), {})
def test_op_map9(self): with self.assertLogHandler('vm', logging.DEBUG) as log_context: # pick item key not found self.econtext.EvaluationStack.PushT(Map(dict={StackItem.New('a'): StackItem.New(4)})) self.econtext.EvaluationStack.PushT(StackItem.New('b')) self.engine.ExecuteOp(OpCode.PICKITEM, self.econtext) self.assertEqual(self.engine.State, VMState.FAULT | VMState.BREAK) self.assertTrue(len(log_context.output) > 0) self.assertTrue('VMFault.DICT_KEY_NOT_FOUND' in log_context.output[0])
def test_op_map9(self, mocked_logger): # pick item key not found self.econtext.EvaluationStack.PushT( Map(dict={StackItem.New('a'): StackItem.New(4)})) self.econtext.EvaluationStack.PushT(StackItem.New('b')) self.engine.ExecuteOp(OpCode.PICKITEM, self.econtext) self.assertEqual(self.engine.State, VMState.FAULT | VMState.BREAK) mocked_logger.assert_called_with( StringIn('VMFault.DICT_KEY_NOT_FOUND'))
def test_op_map12(self): self.econtext.EvaluationStack.PushT( Map( dict={ StackItem.New('a'): StackItem.New(4), StackItem.New('b'): StackItem.New(5) })) self.econtext.EvaluationStack.PushT(StackItem.New('b')) self.engine.ExecuteOp(OpCode.HASKEY, self.econtext) self.assertEqual(self.econtext.EvaluationStack.Items[0].GetBoolean(), True)
def test_interop_map3(self): map = Map({'a': 1, 'b': 2, 'c': 3}) self.assertEqual(map.GetBoolean(), True) with self.assertRaises(Exception) as context: map.GetByteArray() with self.assertRaises(Exception) as context: map.GetBigInteger() map2 = Map({'a': 1, 'b': 2, 'c': 3}) self.assertEqual(map, map2) self.assertTrue(map.Remove('a'), True) self.assertEqual(map.Count, 2) self.assertNotEqual(map, map2) self.assertEqual(map.TryGetValue('b'), (True, 2)) self.assertEqual(map.TryGetValue('h'), (False, None)) map.SetItem('h', 9) self.assertEqual(map.GetItem('h'), 9) self.assertEqual(map.GetMap(), {'b': 2, 'c': 3, 'h': 9})
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 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 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()