def designate_as_role(self, engine: contracts.ApplicationEngine, role: DesignateRole, nodes: List[cryptography.ECPoint]) -> None: if len(nodes) == 0: raise ValueError( "[DesignateContract] Cannot designate empty nodes list") if len(nodes) > 32: raise ValueError( "[DesignateContract] Cannot designate a nodes list larger than 32" ) if not self._check_committee(engine): raise ValueError("[DesignateContract] check committee failed") if engine.snapshot.persisting_block is None: raise ValueError nodes.sort() index = engine.snapshot.persisting_block.index + 1 storage_key = self.create_key( role.to_bytes(1, 'little') + self._to_uint32(index)) with serialization.BinaryWriter() as writer: writer.write_serializable_list(nodes) storage_item = storage.StorageItem(writer.to_array()) engine.snapshot.storages.update(storage_key, storage_item) state = vm.ArrayStackItem(engine.reference_counter) state.append(vm.IntegerStackItem(role.value)) state.append( vm.IntegerStackItem(engine.snapshot.persisting_block.index)) msgrouter.interop_notify(self.hash, "Designation", state)
def _post_transfer(self, engine: contracts.ApplicationEngine, account_from: types.UInt160, account_to: types.UInt160, amount: vm.BigInteger, data: vm.StackItem, call_on_payment: bool) -> None: state = vm.ArrayStackItem(vm.ReferenceCounter()) if account_from == types.UInt160.zero(): state.append(vm.NullStackItem()) else: state.append(vm.ByteStringStackItem(account_from.to_array())) if account_to == types.UInt160.zero(): state.append(vm.NullStackItem()) else: state.append(vm.ByteStringStackItem(account_to.to_array())) state.append(vm.IntegerStackItem(amount)) msgrouter.interop_notify(self.hash, "Transfer", state) # wallet or smart contract if not call_on_payment \ or account_to == types.UInt160.zero() \ or contracts.ManagementContract().get_contract(engine.snapshot, account_to) is None: return if account_from == types.UInt160.zero(): from_: vm.StackItem = vm.NullStackItem() else: from_ = vm.ByteStringStackItem(account_from.to_array()) engine.call_from_native( self.hash, account_to, "onNEP17Payment", [from_, vm.IntegerStackItem(amount), data])
def finish(self, engine: contracts.ApplicationEngine) -> None: tx = engine.script_container tx = cast(payloads.Transaction, tx) response = tx.try_get_attribute(payloads.OracleResponse) if response is None: raise ValueError("Oracle response not found") request = self.get_request(engine.snapshot, response.id) if request is None: raise ValueError("Oracle request not found") state = vm.ArrayStackItem( engine.reference_counter, [vm.IntegerStackItem(response.id), vm.ByteStringStackItem(request.original_tx_id.to_array()) ] ) msgrouter.interop_notify(self.hash, "OracleResponse", state) user_data = contracts.BinarySerializer.deserialize(request.user_data, engine.MAX_STACK_SIZE, engine.reference_counter) args: List[vm.StackItem] = [vm.ByteStringStackItem(request.url.encode()), user_data, vm.IntegerStackItem(int(response.code)), vm.ByteStringStackItem(response.result)] engine.call_from_native(self.hash, request.callback_contract, request.callback_method, args)
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 do_notify(engine: contracts.ApplicationEngine, event_name: bytes, state: vm.ArrayStackItem) -> None: """ Args: engine: event_name: state: values belonging to the notification event. e.g. a NEP-17 transfer event might have as state: from script_hash, to script_hash and an ammount """ if len(event_name) > engine.MAX_EVENT_SIZE: raise ValueError( f"Notify event name length ({len(event_name)}) exceeds maximum allowed ({engine.MAX_EVENT_SIZE})") # will validate size + cyclic references contracts.BinarySerializer.serialize(state, engine.MAX_NOTIFICATION_SIZE) engine.notifications.append((engine.script_container, engine.current_scripthash, event_name, state)) msgrouter.interop_notify(engine.current_scripthash, event_name.decode('utf-8'), state)
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 contract_destroy(self, engine: contracts.ApplicationEngine) -> None: hash_ = engine.calling_scripthash contract = engine.snapshot.contracts.try_get(hash_) if contract is None: return engine.snapshot.contracts.delete(hash_) for key, _ in engine.snapshot.storages.find( contract.id.to_bytes(4, 'little', signed=True)): engine.snapshot.storages.delete(key) msgrouter.interop_notify( self.hash, "Destroy", vm.ArrayStackItem(engine.reference_counter, vm.ByteStringStackItem( contract.hash.to_array())))
def _post_transfer(self, engine: contracts.ApplicationEngine, account_from: types.UInt160, account_to: types.UInt160, token_id: bytes) -> None: state = vm.ArrayStackItem(engine.reference_counter) if account_from == types.UInt160.zero(): state.append(vm.NullStackItem()) else: state.append(vm.ByteStringStackItem(account_from.to_array())) if account_to == types.UInt160.zero(): state.append(vm.NullStackItem()) else: state.append(vm.ByteStringStackItem(account_to.to_array())) state.append(vm.IntegerStackItem(1)) state.append(vm.ByteStringStackItem(token_id)) msgrouter.interop_notify(self.hash, "Transfer", state) if account_to != types.UInt160.zero() and \ contracts.ManagementContract().get_contract(engine.snapshot, account_to) is not None: engine.call_from_native(self.hash, account_to, "onNEP17Payment", list(state))
def _request(self, engine: contracts.ApplicationEngine, url: str, filter: str, callback: str, user_data: vm.StackItem, gas_for_response: int) -> None: if len(url.encode('utf-8')) > self._MAX_URL_LENGTH or \ len(filter.encode('utf-8')) > self._MAX_FILTER_LEN or \ len(callback.encode('utf-8')) > self._MAX_CALLBACK_LEN or \ callback.startswith("_") or \ gas_for_response < 10000000: raise ValueError engine.add_gas(self.get_price(engine.snapshot)) engine.add_gas(gas_for_response) self._gas.mint(engine, self.hash, vm.BigInteger(gas_for_response), False) si_item_id = engine.snapshot.storages.get(self.key_request_id, read_only=False) item_id = vm.BigInteger(si_item_id.value) si_item_id.value = (item_id + 1).to_array() if contracts.ManagementContract().get_contract(engine.snapshot, engine.calling_scripthash) is None: raise ValueError oracle_request = OracleRequest(self._get_original_txid(engine), gas_for_response, url, filter, engine.calling_scripthash, callback, contracts.BinarySerializer.serialize(user_data, self._MAX_USER_DATA_LEN)) engine.snapshot.storages.put(self.key_request + int(item_id).to_bytes(8, 'little', signed=False), storage.StorageItem(oracle_request.to_array()) ) sk_id_list = self.key_id_list + self._get_url_hash(url) si_id_list = engine.snapshot.storages.try_get(sk_id_list, read_only=False) if si_id_list is None: si_id_list = storage.StorageItem(b'\x00') with serialization.BinaryReader(si_id_list.value) as reader: count = reader.read_var_int() id_list = [] for _ in range(count): id_list.append(reader.read_uint64()) id_list.append(item_id) if len(id_list) >= 256: raise ValueError("Oracle has too many pending responses for this url") with serialization.BinaryWriter() as writer: writer.write_var_int(len(id_list)) for id in id_list: writer.write_uint64(id) si_id_list.value = writer.to_array() engine.snapshot.storages.update(sk_id_list, si_id_list) state = vm.ArrayStackItem( engine.reference_counter, [vm.IntegerStackItem(item_id), vm.ByteStringStackItem(engine.calling_scripthash.to_array()), vm.ByteStringStackItem(url.encode()), vm.ByteStringStackItem(filter.encode()), ] ) msgrouter.interop_notify(self.hash, "OracleRequest", state)