def to_stack_item(self, reference_counter: vm.ReferenceCounter) -> vm.StackItem: array = vm.ArrayStackItem(reference_counter) script = vm.ByteStringStackItem(self.script) manifest = vm.ByteStringStackItem(str(self.manifest)) has_storage = vm.BooleanStackItem(self.has_storage) is_payable = vm.BooleanStackItem(self.is_payable) array.append([script, manifest, has_storage, is_payable]) return array
def test_deserialize_struct(self): s = vm.StructStackItem(self.reference_counter) bool1 = vm.BooleanStackItem(True) bool2 = vm.BooleanStackItem(False) s.append([bool1, bool2]) s_serialized = contracts.BinarySerializer.serialize(s, 999) new_s = contracts.BinarySerializer.deserialize(s_serialized, 999, self.reference_counter) self.assertIsInstance(new_s, vm.StructStackItem) for l, r in zip(new_s._items, s._items): self.assertEqual(l, r)
def test_verify_secp256k1(self): """ byte[] message = System.Text.Encoding.Default.GetBytes("hello"); byte[] signature = "5331be791532d157df5b5620620d938bcb622ad02c81cfc184c460efdad18e695480d77440c511e9ad02ea30d773cb54e88f8cbb069644aefa283957085f38b5".HexToBytes(); byte[] pubKey = "03ea01cb94bdaf0cd1c01b159d474f9604f4af35a3e2196f6bdfdb33b2aa4961fa".HexToBytes(); Crypto.VerifySignature(message, signature, pubKey, Neo.Cryptography.ECC.ECCurve.Secp256k1).Should().BeTrue(); """ message = b'hello' signature = binascii.unhexlify( b'5331be791532d157df5b5620620d938bcb622ad02c81cfc184c460efdad18e695480d77440c511e9ad02ea30d773cb54e88f8cbb069644aefa283957085f38b5' ) public_key = binascii.unhexlify( b'03ea01cb94bdaf0cd1c01b159d474f9604f4af35a3e2196f6bdfdb33b2aa4961fa' ) self.assertTrue( cryptography.verify_signature(message, signature, public_key, cryptography.ECCCurve.SECP256K1)) sb = vm.ScriptBuilder() sb.emit_push(signature) sb.emit_push(public_key) sb.emit_push(message) sb.emit_syscall( syscall_name_to_int("Neo.Crypto.VerifyWithECDsaSecp256k1")) engine = test_engine() script = vm.Script(sb.to_array()) engine.load_script(script) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(True), engine.result_stack.pop()) # again with bad signature bad_signature = b'\xFF' + signature[1:] sb = vm.ScriptBuilder() sb.emit_push(bad_signature) sb.emit_push(public_key) sb.emit_push(message) sb.emit_syscall( syscall_name_to_int("Neo.Crypto.VerifyWithECDsaSecp256k1")) engine = test_engine() script = vm.Script(sb.to_array()) engine.load_script(script) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(False), engine.result_stack.pop())
def test_check_multisig_with_ECDSA_Secp256k1(self): # values taken from test_verify_secp256k1() engine = test_engine() message = vm.ByteStringStackItem(b'hello') signature = vm.ByteStringStackItem( binascii.unhexlify( b'5331be791532d157df5b5620620d938bcb622ad02c81cfc184c460efdad18e695480d77440c511e9ad02ea30d773cb54e88f8cbb069644aefa283957085f38b5' )) signatures = vm.ArrayStackItem(engine.reference_counter) signatures.append(signature) public_keys = vm.ArrayStackItem(engine.reference_counter) public_key = vm.ByteStringStackItem( binascii.unhexlify( b'03ea01cb94bdaf0cd1c01b159d474f9604f4af35a3e2196f6bdfdb33b2aa4961fa' )) public_keys.append(public_key) sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("Neo.Crypto.CheckMultisigWithECDsaSecp256k1")) script = vm.Script(sb.to_array()) engine.load_script(script) engine.push(signatures) engine.push(public_keys) engine.push(message) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(True), engine.result_stack.pop())
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 _native_to_stackitem(self, value, native_type) -> vm.StackItem: """ Convert native type to VM type Note: order of checking matters. e.g. a Transaction should be treated as IInteropable, while its also ISerializable """ if isinstance(value, vm.StackItem): return value elif value is None: return vm.NullStackItem() elif native_type in [int, vm.BigInteger]: return vm.IntegerStackItem(value) elif issubclass(native_type, IInteroperable): value_ = cast(IInteroperable, value) return value_.to_stack_item(self.reference_counter) elif issubclass(native_type, serialization.ISerializable): serializable_value = cast(serialization.ISerializable, value) return vm.ByteStringStackItem(serializable_value.to_array()) # mypy bug? https://github.com/python/mypy/issues/9756 elif native_type in [bytes, bytearray]: # type: ignore return vm.ByteStringStackItem(value) elif native_type == str: return vm.ByteStringStackItem(bytes(value, 'utf-8')) elif native_type == bool: return vm.BooleanStackItem(value) elif issubclass(native_type, (enum.IntFlag, enum.IntEnum)): return self._native_to_stackitem(value.value, int) else: return vm.StackItem.from_interface(value)
def test_serialization_array(self): b = vm.BooleanStackItem(True) bs = vm.ByteStringStackItem("test") i = vm.IntegerStackItem(123) n = vm.NullStackItem() ref_ctr = vm.ReferenceCounter() a = vm.ArrayStackItem(ref_ctr) a.append([b, bs, i, n]) expected = r'[true,"test",123,null]' self.assertEqual(expected, contracts.JSONSerializer.serialize(a, 999))
def to_stack_item(self, reference_counter: vm.ReferenceCounter) -> vm.StackItem: struct = cast( vm.StructStackItem, super(ContractMethodDescriptor, self).to_stack_item(reference_counter)) struct.append(vm.IntegerStackItem(self.return_type.value)) struct.append(vm.IntegerStackItem(self.offset)) struct.append(vm.BooleanStackItem(self.safe)) return struct
def contract_create_with_data( self, engine: contracts.ApplicationEngine, nef_file: bytes, manifest: bytes, data: vm.StackItem) -> contracts.ContractState: if not isinstance(engine.script_container, payloads.Transaction): raise ValueError( "Cannot create contract without a Transaction script container" ) nef_len = len(nef_file) manifest_len = len(manifest) if (nef_len == 0 or nef_len > engine.MAX_CONTRACT_LENGTH or manifest_len == 0 or manifest_len > contracts.ContractManifest.MAX_LENGTH): raise ValueError("Invalid NEF or manifest length") engine.add_gas( max(engine.STORAGE_PRICE * (nef_len + manifest_len), self.get_minimum_deployment_fee(engine.snapshot))) nef = contracts.NEF.deserialize_from_bytes(nef_file) parsed_manifest = contracts.ContractManifest.from_json( json.loads(manifest.decode())) self.validate(nef.script, parsed_manifest.abi) sb = vm.ScriptBuilder() sb.emit(vm.OpCode.ABORT) sb.emit_push(engine.script_container.sender.to_array()) sb.emit_push(nef.checksum) sb.emit_push(parsed_manifest.name) hash_ = to_script_hash(sb.to_array()) existing_contract = engine.snapshot.contracts.try_get(hash_) if existing_contract is not None: raise ValueError("Contract already exists") contract = contracts.ContractState( self.get_next_available_id(engine.snapshot), nef, parsed_manifest, 0, hash_) if not contract.manifest.is_valid(hash_): raise ValueError("Error: invalid manifest") engine.snapshot.contracts.put(contract) method_descriptor = contract.manifest.abi.get_method("_deploy", 2) if method_descriptor is not None: engine.call_from_native(self.hash, hash_, method_descriptor.name, [data, vm.BooleanStackItem(False)]) msgrouter.interop_notify( self.hash, "Deploy", vm.ArrayStackItem(engine.reference_counter, vm.ByteStringStackItem( contract.hash.to_array()))) return contract
def test_serialization_array_nested(self): bool_t = vm.BooleanStackItem(True) bool_f = vm.BooleanStackItem(False) bs1 = vm.ByteStringStackItem("test1") bs2 = vm.ByteStringStackItem("test2") i1 = vm.IntegerStackItem(123) i2 = vm.IntegerStackItem(321) ref_ctr = vm.ReferenceCounter() a1 = vm.ArrayStackItem(ref_ctr) a1.append([bool_t, bs1, i1]) a2 = vm.ArrayStackItem(ref_ctr) a2.append([bool_f, bs2, i2]) parent = vm.ArrayStackItem(ref_ctr) parent.append([a1, a2]) expected = r'[[true,"test1",123],[false,"test2",321]]' self.assertEqual(expected, contracts.JSONSerializer.serialize(parent, 999))
def test_check_multisig_with_ECDSA_Secp256r1(self): engine = test_engine() message = vm.ByteStringStackItem(b'hello') kp1 = cryptography.KeyPair(private_key=b'\x01' * 32) sig1 = cryptography.sign(message.to_array(), kp1.private_key) sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("Neo.Crypto.CheckMultisigWithECDsaSecp256r1")) script = vm.Script(sb.to_array()) engine.load_script(script) # setup the stack for the syscall signatures = vm.ArrayStackItem(engine.reference_counter) signatures.append(vm.ByteStringStackItem(sig1)) public_keys = vm.ArrayStackItem(engine.reference_counter) public_keys.append( vm.ByteStringStackItem(kp1.public_key.encode_point(False))) engine.push(signatures) engine.push(public_keys) engine.push(message) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(True), engine.result_stack.pop()) # do the same but change the message such that the signature and key are wrong engine = test_engine() engine.load_script(script) engine.push(signatures) engine.push(public_keys) engine.push(vm.ByteStringStackItem(b'badmessage')) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(False), engine.result_stack.pop())
def test_verify_secp256r1(self): """ var privkey = new byte[] { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; var message = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; var signature = new byte[] { 56,70,104,22,234,182,23,161,111,25,71,188,12,5,54,28,99,189,8,47,4,82,62,150,57,216,25,130,217,25,123,118,89,149,217,130,12,109,34,125,176,189,142,119,154,140,116,16,32,209,214,87,178,248,214,39,248,29,214,10,205,153,146,111}; var kp = new KeyPair(privkey); Console.WriteLine(Crypto.VerifySignature(message, signature, kp.PublicKey.EncodePoint(false), ECCurve.Secp256r1)); """ message = b'\x01' * 32 priv_key = b'\x02' + b'\x00' * 30 + b'\x01' sig = cryptography.sign(message, priv_key) # from ecdsa import VerifyingKey, SigningKey, curves as ecdsa_curves # import hashlib # sk = SigningKey.from_string(priv_key, curve=ecdsa_curves.NIST256p, hashfunc=hashlib.sha256) # sig = sk.sign(message, hashfunc=hashlib.sha256) kp = cryptography.KeyPair(priv_key) sb = vm.ScriptBuilder() sb.emit_push(sig) sb.emit_push(kp.public_key.encode_point(False)) sb.emit_push(message) sb.emit_syscall( syscall_name_to_int("Neo.Crypto.VerifyWithECDsaSecp256r1")) engine = test_engine() script = vm.Script(sb.to_array()) engine.load_script(script) # first test with an invalid interop item. They must be IVerifiable engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) self.assertEqual(vm.BooleanStackItem(True), engine.result_stack.pop())
def contract_update_with_data(self, engine: contracts.ApplicationEngine, nef_file: bytes, manifest: bytes, data: vm.StackItem) -> None: nef_len = len(nef_file) manifest_len = len(manifest) engine.add_gas(engine.STORAGE_PRICE * (nef_len + manifest_len)) contract = engine.snapshot.contracts.try_get(engine.calling_scripthash, read_only=False) if contract is None: raise ValueError("Can't find contract to update") if nef_len == 0: raise ValueError(f"Invalid NEF length: {nef_len}") # update contract contract.nef = contracts.NEF.deserialize_from_bytes(nef_file) if manifest_len == 0 or manifest_len > contracts.ContractManifest.MAX_LENGTH: raise ValueError(f"Invalid manifest length: {manifest_len}") manifest_new = contracts.ContractManifest.from_json( json.loads(manifest.decode())) if manifest_new.name != contract.manifest.name: raise ValueError("Error: cannot change contract name") if not contract.manifest.is_valid(contract.hash): raise ValueError("Error: manifest does not match with script") contract.manifest = manifest_new self.validate(contract.nef.script, contract.manifest.abi) contract.update_counter += 1 if len(nef_file) != 0: method_descriptor = contract.manifest.abi.get_method("_deploy", 2) if method_descriptor is not None: engine.call_from_native(self.hash, contract.hash, method_descriptor.name, [data, vm.BooleanStackItem(True)]) msgrouter.interop_notify( self.hash, "Update", vm.ArrayStackItem(engine.reference_counter, vm.ByteStringStackItem( contract.hash.to_array())))
def _native_to_stackitem(self, value, native_type) -> vm.StackItem: """ Convert native type to VM type Note: order of checking matters. e.g. a Transaction should be treated as IInteropable, while its also ISerializable """ if isinstance(value, vm.StackItem): return value elif value is None: return vm.NullStackItem() elif native_type in [int, vm.BigInteger]: return vm.IntegerStackItem(value) elif issubclass(native_type, IInteroperable): value_ = cast(IInteroperable, value) return value_.to_stack_item(self.reference_counter) elif issubclass(native_type, serialization.ISerializable): serializable_value = cast(serialization.ISerializable, value) return vm.ByteStringStackItem(serializable_value.to_array()) # mypy bug? https://github.com/python/mypy/issues/9756 elif native_type in [bytes, bytearray]: # type: ignore return vm.ByteStringStackItem(value) elif native_type == str: return vm.ByteStringStackItem(bytes(value, 'utf-8')) elif native_type == bool: return vm.BooleanStackItem(value) elif issubclass(native_type, (enum.IntFlag, enum.IntEnum)): return self._native_to_stackitem(value.value, int) elif hasattr(native_type, '__origin__' ) and native_type.__origin__ == Union: # type: ignore # handle typing.Optional[type], Optional is an alias for Union[x, None] # only support specifying 1 type if len(native_type.__args__) != 2: raise ValueError( f"Don't know how to convert native type {native_type} to stackitem" ) for i in native_type.__args__: if i is None: continue return self._native_to_stackitem(value, native_type) else: raise ValueError # shouldn't be possible, but silences mypy else: return vm.StackItem.from_interface(value)
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 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()