def test_with_map(self): """ var m = new Map(new ReferenceCounter()); var i = new Integer(new BigInteger(1)); var i2 = new Integer(new BigInteger(2)); var b = new Neo.VM.Types.Boolean(true); m[i] = b; m[i2] = b; """ # moved outside of multiline comment because pycharm is broken: https://youtrack.jetbrains.com/issue/PY-43117 #Console.WriteLine($"b'\\x{BitConverter.ToString(BinarySerializer.Serialize(m, 999)).Replace("-", @"\x")}'"); m = vm.MapStackItem(self.reference_counter) i = vm.IntegerStackItem(vm.BigInteger(1)) i2 = vm.IntegerStackItem(vm.BigInteger(2)) b = vm.BooleanStackItem(True) m[i] = b m[i2] = b expected = b'\x48\x02\x21\x01\x01\x20\x01\x21\x01\x02\x20\x01' out = contracts.BinarySerializer.serialize(m, 999) self.assertEqual(expected, out) # now we add a reference to ourselves. with self.assertRaises(ValueError) as context: m[i] = m contracts.BinarySerializer.serialize(m, 999) self.assertEqual("Item already exists", str(context.exception)) # now test deserialization m[i] = b # restore m[i] to original content new_m = contracts.BinarySerializer.deserialize(out, 2048, self.reference_counter) self.assertEqual(len(m), len(new_m)) self.assertEqual(m.keys(), new_m.keys()) self.assertEqual(m.values(), new_m.values())
def test_iterator_create_from_map(self): engine = test_engine() map_item = vm.MapStackItem(engine.reference_counter) key1 = vm.IntegerStackItem(1) key2 = vm.IntegerStackItem(3) item1 = vm.IntegerStackItem(2) item2 = vm.IntegerStackItem(4) map_item[key1] = item1 map_item[key2] = item2 engine.push(map_item) r = engine.invoke_syscall_by_name("System.Iterator.Create") self.assertIsInstance(r, MapWrapper) self.assertTrue(r.next()) value = r.value() self.assertIsInstance(value, vm.StructStackItem) self.assertEqual(2, len(value)) self.assertEqual(key1, value[0]) self.assertEqual(item1, value[1]) self.assertTrue(r.next()) value = r.value() self.assertIsInstance(value, vm.StructStackItem) self.assertEqual(2, len(value)) self.assertEqual(key2, value[0]) self.assertEqual(item2, value[1]) # exhausted the iterator self.assertFalse(r.next()) with self.assertRaises(ValueError) as context: r.value() self.assertEqual( "Cannot call 'value' without having advanced the iterator at least once", str(context.exception))
def to_stack_item(self, reference_counter: vm.ReferenceCounter) -> vm.StackItem: struct = vm.StructStackItem(reference_counter) struct.append(vm.ByteStringStackItem(self.name)) struct.append(vm.ArrayStackItem(reference_counter, list(map(lambda g: g.to_stack_item(reference_counter), self.groups))) ) struct.append(vm.MapStackItem(reference_counter)) struct.append(vm.ArrayStackItem(reference_counter, list(map(lambda s: vm.ByteStringStackItem(s), self.supported_standards))) ) struct.append(self.abi.to_stack_item(reference_counter)) struct.append(vm.ArrayStackItem(reference_counter, list(map(lambda p: p.to_stack_item(reference_counter), self.permissions))) ) if self.trusts.is_wildcard: struct.append(vm.NullStackItem()) else: struct.append( vm.ArrayStackItem(reference_counter, list(map(lambda t: vm.ByteStringStackItem(t.to_array()), self.trusts))) # type: ignore ) if self.extra is None: struct.append(vm.ByteStringStackItem("null")) else: struct.append(vm.ByteStringStackItem(json.dumps(self.extra))) return struct
def test_serialization_map_with_integer_key(self): i = vm.IntegerStackItem(123) v = vm.IntegerStackItem(321) ref_ctr = vm.ReferenceCounter() m = vm.MapStackItem(ref_ctr) m[i] = v expected = r'{"123":321}' s = contracts.JSONSerializer.serialize(m, 999) self.assertEqual(expected, s)
def test_notify_state_helper_map(self): bssi = vm.ByteStringStackItem(b'\x01\x02') # 2 primitive = vm.IntegerStackItem(2) # 1 engine = test_engine() map1 = vm.MapStackItem(engine.reference_counter) map1[primitive] = bssi self.assertEqual(3, _validate_state_item_limits(engine, map1)) # self reference map1[primitive] = map1 # asserting to 1 because the key in the map has a length of 1 self.assertEqual(1, _validate_state_item_limits(engine, map1))
def test_serialization_basics(self): ref = vm.ReferenceCounter() m = vm.MapStackItem(ref) s = contracts.JSONSerializer.serialize(m, 999) self.assertEqual("{}", s) a = vm.ArrayStackItem(ref) s = contracts.JSONSerializer.serialize(a, 999) self.assertEqual(r'[]', s) i1 = vm.IntegerStackItem(1) i2 = vm.IntegerStackItem(9007199254740992) a.append([i1, i2]) s = contracts.JSONSerializer.serialize(a, 999) self.assertEqual(r'[1,"9007199254740992"]', s)
def test_serialization_map(self): ref = vm.ReferenceCounter() key1 = vm.ByteStringStackItem("test1") key2 = vm.ByteStringStackItem("test2") key3 = vm.ByteStringStackItem("test3") v1 = vm.IntegerStackItem(1) v2 = vm.IntegerStackItem(2) v3 = vm.IntegerStackItem(3) m = vm.MapStackItem(ref) m[key1] = v1 m[key3] = v3 m[key2] = v2 s = contracts.JSONSerializer.serialize(m, 999) # this is a known deviation. NEO preserved key order, we don't # but shouldn't matter as it gets deserialized to a map stackitem expected = r'{"test1":1,"test2":2,"test3":3}' self.assertEqual(expected, s)
def test_iterator_key_and_value(self): engine = test_engine() # same map setup code as test_iterator_create_from_map() map_item = vm.MapStackItem(engine.reference_counter) key1 = vm.IntegerStackItem(1) key2 = vm.IntegerStackItem(3) item1 = vm.IntegerStackItem(2) item2 = vm.IntegerStackItem(4) map_item[key1] = item1 map_item[key2] = item2 # we build a script such that we can create an interator, move it forward and retrieves the key and values script = vm.ScriptBuilder() # initialize 1 slot to store the iterator in script.emit(vm.OpCode.INITSLOT) script.emit_raw(b'\x01\x00') script.emit_syscall(syscall_name_to_int("System.Iterator.Create")) # save the iterator and retrieve it again script.emit(vm.OpCode.STLOC0) script.emit(vm.OpCode.LDLOC0) script.emit_syscall(syscall_name_to_int("System.Enumerator.Next")) # clear the result of `Enumerator.Next()`, we assume it will say True script.emit(vm.OpCode.DROP) script.emit(vm.OpCode.LDLOC0) script.emit_syscall(syscall_name_to_int("System.Iterator.Key")) script.emit(vm.OpCode.LDLOC0) script.emit_syscall(syscall_name_to_int("System.Enumerator.Value")) engine.load_script(vm.Script(script.to_array())) engine.push(map_item) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) # we expect a key and a value on their self.assertEqual(len(engine.result_stack), 2) # value was put on last, comes of first value = engine.result_stack.pop() self.assertEqual(item1, value) key = engine.result_stack.pop() self.assertEqual(key1, key)
def deserialize( json_data: JObject, reference_counter: vm.ReferenceCounter = None) -> vm.StackItem: """ Deserialize JSON into a virtual machine stack item """ t = type(json_data) if t == dict: json_data = cast(dict, json_data) if reference_counter is None: raise ValueError( "Can't deserialize JSON object without reference counter") map_item = vm.MapStackItem(reference_counter) for k, v in json_data.items(): key = vm.ByteStringStackItem(k) value = JSONSerializer.deserialize(v, reference_counter) map_item[key] = value return map_item elif t == list: if reference_counter is None: raise ValueError( "Can't deserialize JSON array without reference counter") array_item = vm.ArrayStackItem(reference_counter) json_data = cast(list, json_data) elements = [ JSONSerializer.deserialize(e, reference_counter) for e in json_data ] array_item.append(elements) return array_item elif json_data is None: return vm.NullStackItem() elif t == str: if json_data == "null": return vm.NullStackItem() return vm.ByteStringStackItem(json_data) # type: ignore elif t == int: return vm.IntegerStackItem(json_data) # type: ignore elif t == bool: json_data = cast(bool, json_data) return vm.BooleanStackItem(json_data) else: # should never happen or somebody ignored the type checker output raise ValueError()
def test_iterator_keys(self): # we iterate over just the 2 keys in our map engine = test_engine() map_item = vm.MapStackItem(engine.reference_counter) key1 = vm.IntegerStackItem(1) key2 = vm.IntegerStackItem(3) item1 = vm.IntegerStackItem(2) item2 = vm.IntegerStackItem(4) map_item[key1] = item1 map_item[key2] = item2 script = vm.ScriptBuilder() # initialize 1 slot to store the iterator in script.emit(vm.OpCode.INITSLOT) script.emit_raw(b'\x01\x00') script.emit_syscall(syscall_name_to_int("System.Iterator.Create")) script.emit_syscall(syscall_name_to_int("System.Iterator.Keys")) script.emit(vm.OpCode.STLOC0) # we have 2 keys in our map, so we can repeat this sequence twice for _ in range(2): script.emit(vm.OpCode.LDLOC0) script.emit_syscall(syscall_name_to_int("System.Enumerator.Next")) script.emit(vm.OpCode.DROP) script.emit(vm.OpCode.LDLOC0) script.emit_syscall(syscall_name_to_int("System.Enumerator.Value")) engine.load_script(vm.Script(script.to_array())) engine.push(map_item) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) # we expect a 2 keys on there self.assertEqual(len(engine.result_stack), 2) # key2 was put on last, comes of first key2_from_engine = engine.result_stack.pop() self.assertEqual(key2, key2_from_engine) key1_from_engine = engine.result_stack.pop() self.assertEqual(key1, key1_from_engine)
def deserialize(data: bytes, max_size: int, max_item_size: int, reference_counter: vm.ReferenceCounter) -> vm.StackItem: """ Deserialize data into a stack item. Args: data: byte array of a serialized stack item. max_size: data reading limit for Array, Struct and Map types. max_item_size: data reading limit for ByteString or Buffer types. reference_counter: a valid reference counter instance. Get's passed into reference stack items. """ if len(data) == 0: raise ValueError("Nothing to deserialize") deserialized: List[Union[vm.StackItem, PlaceHolder]] = [] to_deserialize = 1 with serialization.BinaryReader(data) as reader: while not to_deserialize == 0: to_deserialize -= 1 item_type = vm.StackItemType(reader.read_byte()[0]) if item_type == vm.StackItemType.ANY: deserialized.append(vm.NullStackItem()) elif item_type == vm.StackItemType.BOOLEAN: deserialized.append(vm.BooleanStackItem( reader.read_bool())) elif item_type == vm.StackItemType.INTEGER: deserialized.append( vm.IntegerStackItem( vm.BigInteger( reader.read_var_bytes( vm.IntegerStackItem.MAX_SIZE)))) elif item_type == vm.StackItemType.BYTESTRING: deserialized.append( vm.ByteStringStackItem( reader.read_var_bytes(max_item_size))) elif item_type == vm.StackItemType.BUFFER: deserialized.append( vm.BufferStackItem( reader.read_var_bytes(max_item_size))) elif item_type in [ vm.StackItemType.ARRAY, vm.StackItemType.STRUCT ]: count = reader.read_var_int(max_size) deserialized.append(PlaceHolder(item_type, count)) to_deserialize += count elif item_type == vm.StackItemType.MAP: count = reader.read_var_int(max_size) deserialized.append(PlaceHolder(item_type, count)) to_deserialize += count * 2 else: raise ValueError("Invalid format") temp: List[vm.StackItem] = [] while len(deserialized) > 0: item = deserialized.pop() if type(item) == PlaceHolder: item = cast(PlaceHolder, item) if item.type == vm.StackItemType.ARRAY: array = vm.ArrayStackItem(reference_counter) for _ in range(0, item.count): array.append(temp.pop()) temp.append(array) elif item.type == vm.StackItemType.STRUCT: struct = vm.StructStackItem(reference_counter) for _ in range(0, item.count): struct.append(temp.pop()) temp.append(struct) elif item.type == vm.StackItemType.MAP: m = vm.MapStackItem(reference_counter) for _ in range(0, item.count): k = temp.pop() k = cast(vm.PrimitiveType, k) v = temp.pop() m[k] = v temp.append(m) else: item = cast(vm.StackItem, item) temp.append(item) return temp.pop()
def properties(self, engine: contracts.ApplicationEngine, token_id: bytes) -> vm.MapStackItem: storage_item = engine.snapshot.storages.get(self.key_token + token_id, read_only=True) map_ = vm.MapStackItem(engine.reference_counter) for k, v in NFTState.deserialize_from_bytes(storage_item.value).to_json(): map_[k] = v return map_