class EthereumWorkerRegistryListImpl(WorkerRegistryList): """ This class provide APIs to read/write registry entries of workers, which is stored in the Ethereum blockchain. """ def __init__(self, config): if self.__validate(config): self.__initialize(config) else: raise Exception("Invalid or missing config parameter") def registry_lookup(self, app_type_id=None): """ Registry Lookup identified by application type ID. Parameters: app_type_id Application type ID to lookup in the registry Returns: Returns tuple containing totalCount, lookupTag, ids on success: totalCount Total number of entries matching a specified lookup criteria. If this number is larger than the size of the IDs array, the caller should use the lookupTag to call workerLookUpNext to retrieve the rest of the IDs lookupTag Optional parameter. If it is returned, it means that there are more matching registry IDs that can be retrieved by calling the function registry_lookup_next with this tag as an input parameter ids Array of the registry organization IDs that match the input parameters Returns None on error. """ if (self.__contract_instance is not None): if app_type_id is not None: if is_valid_hex_str( binascii.hexlify(app_type_id).decode("utf8")): lookupResult = \ self.__contract_instance.functions.registryLookUp( app_type_id).call() else: logging.info( "Invalid application type id {}".format(app_type_id)) return None else: lookup_result = \ self.__contract_instance.functions.registryLookUp(b"") \ .call() return lookup_result else: logging.error( "direct registry contract instance is not initialized") return None def registry_retrieve(self, org_id): """ Retrieving Registry Information identified by organization ID. Parameters: org_id Organization ID to lookup Returns: Tuple containing following on success: uri string defining a URI for this registry that supports the Off-Chain Worker Registry JSON RPC API. It will be None for the proxy model sc_addr Ethereum address for worker registry smart contract address application_type_ids List of application ids(array of byte[]) status Status of the registry Returns None on error. """ if (self.__contract_instance is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.info("Invalid Org id {}".format(org_id)) return None else: registry_details = \ self.__contract_instance.functions.registryRetrieve( org_id).call() return registry_details else: logging.error( "direct registry contract instance is not initialized") return None def registry_lookup_next(self, app_type_id, lookup_tag): """ Get additional registry lookup results. This function is called to retrieve additional results of the Registry lookup initiated by the registry_lookUp call. Parameters: app_type_id Application type that has to be supported by the workers retrieved lookup_tag Returned by a previous call to either this function or to registry_lookup Returns: Outputs tuple on success containing the following: total_count Total number of entries matching the lookup criteria. If this number is larger than the number of IDs returned so far, the caller should use lookup_tag to call registry_lookup_next to the rest of the ids new_lookup_tag Optional parameter. If it is returned, it means that there are more matching registry IDs that can be retrieved by calling this function again with this tag as an input parameter ids Array of the registry IDs that match the input parameters Returns None on error. """ if (self.__contract_instance is not None): if is_valid_hex_str(binascii.hexlify(app_type_id).decode("utf8")): lookup_result = \ self.__contract_instance.functions.registryLookUpNext( app_type_id, lookup_tag).call() return lookup_result else: logging.info( "Invalid application type id {}".format(app_type_id)) return None else: logging.error( "direct registry contract instance is not initialized") return None def __validate(self, config): """ Validates parameter from config parameters for existence. Parameters: config Configuration parameters to validate Returns: true on success or false if validation fails. """ if config["ethereum"]["direct_registry_contract_file"] is None: logging.error("Missing direct registry contract file path!!") return False if config["ethereum"]["direct_registry_contract_address"] is None: logging.error("Missing direct registry contract address!!") return False return True def __initialize(self, config): """ Initialize the parameters from config to instance variables. """ self.__eth_client = EthereumWrapper(config) tcf_home = environ.get("TCF_HOME", "../../../") contract_file_name = tcf_home + "/" + \ config["ethereum"]["direct_registry_contract_file"] contract_address = \ config["ethereum"]["direct_registry_contract_address"] self.__contract_instance = self.__eth_client.get_contract_instance( contract_file_name, contract_address) def registry_add(self, org_id, uri, sc_addr, app_type_ids): """ Add a new registry. Parameters: org_id bytes[] identifies organization that hosts the registry, e.g. a bank in the consortium or an anonymous entity uri String defines a URI for this registry that supports the Off-Chain Worker Registry JSON RPC API. sc_addr bytes[] defines an Ethereum address that runs the Worker Registry Smart Contract API smart contract for this registry app_type_ids []bytes[] is an optional parameter that defines application types supported by the worker managed by the registry Returns: Transaction receipt on success or None on error. """ if (self.__contract_instance is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.info("Invalid Org id {}".format(org_id)) return None if (sc_addr is not None and is_valid_hex_str( binascii.hexlify(sc_addr).decode("utf8")) is False): logging.info("Invalid smart contract address {}") return None if (not uri): logging.info("Empty uri {}".format(uri)) return None for aid in app_type_ids: if (is_valid_hex_str(binascii.hexlify(aid).decode("utf8")) is False): logging.info("Invalid application id {}".format(aid)) return None txn_dict = self.__contract_instance.functions.registryAdd( org_id, uri, org_id, app_type_ids).buildTransaction( self.__eth_client.get_transaction_params()) txn_receipt = self.__eth_client.execute_transaction(txn_dict) return txn_receipt else: logging.error( "direct registry contract instance is not initialized") return None def registry_update(self, org_id, uri, sc_addr, app_type_ids): """ Update a registry. Parameters: org_id bytes[] identifies organization that hosts the registry, e.g. a bank in the consortium or an anonymous entity uri string defines a URI for this registry that supports the Off-Chain Worker Registry JSON RPC API sc_addr bytes[] defines an Ethereum address that runs a Worker Registry Smart Contract API smart contract for this registry app_type_ids []bytes[] is an optional parameter that defines application types supported by the worker managed by the registry Returns: Transaction receipt on success or None on error. """ if (self.__contract_instance is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.error("Invalid Org id {}".format(org_id)) return None if (sc_addr is not None and is_valid_hex_str( binascii.hexlify(sc_addr).decode("utf8")) is False): logging.error( "Invalid smart contract address {}".format(sc_addr)) return None if (not uri): logging.error("Empty uri {}".format(uri)) return None for aid in app_type_ids: if (is_valid_hex_str(binascii.hexlify(aid).decode("utf8")) is False): logging.error("Invalid application id {}".format(aid)) return None txn_dict = \ self.__contract_instance.functions.registryUpdate( org_id, uri, sc_addr, app_type_ids).buildTransaction( self.__eth_client.get_transaction_params() ) txn_receipt = self.__eth_client.execute_transaction(txn_dict) return txn_receipt else: logging.error( "direct registry contract instance is not initialized") return None def registry_set_status(self, org_id, status): """ Set registry status. Parameters: org_id bytes[] identifies organization that hosts the registry status Defines registry status to set. The currently defined values are: 1 - the registry is active 2 - the registry is temporarily "off-line" 3 - the registry is decommissioned Returns: Transaction receipt on success or None on error. """ if (self.__contract_instance is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.info("Invalid Org id {}".format(org_id)) return None if not isinstance(status, RegistryStatus): logging.info("Invalid registry status {}".format(status)) return None txn_dict = \ self.__contract_instance.functions.registrySetStatus( org_id, status.value).buildTransaction( self.__eth_client.get_transaction_params() ) txn_receipt = self.__eth_client.execute_transaction(txn_dict) return txn_receipt else: logging.error( "direct registry contract instance is not initialized") return None
class EthereumWorkOrderProxyImpl(WorkOrderProxy): """ This class is meant to write work order-related data to the Ethereum blockchain. Detailed method descriptions are available in the interfaces. """ def __init__(self, config): if self.__validate(config) is True: self.__initialize(config) else: raise Exception("Invalid configuration parameter") def __validate(self, config): """ Validate configuration parameters for existence. Parameters: config Configuration parameters Returns: True if validation succeeds or false if validation fails. """ try: if config["ethereum"]["work_order_contract_file"] is None: logging.error("Missing work order contract file path!!") return False if config["ethereum"]["work_order_contract_address"] is None: logging.error("Missing work order contract address!!") return False if config["ethereum"]["provider"] is None: logging.error("Missing Ethereum provider url!!") return False except KeyError as ex: logging.error("Required configs not present".format(ex)) return False return True def __initialize(self, config): """ Initialize the parameters from config to instance variables. Parameters: config Configuration parameters to initialize """ self.__eth_client = EthereumWrapper(config) self._config = config tcf_home = environ.get("TCF_HOME", "../../../") contract_file_name = tcf_home + "/" + \ config["ethereum"]["work_order_contract_file"] contract_address = \ config["ethereum"]["work_order_contract_address"] self.__contract_instance, self.__contract_instance_evt =\ self.__eth_client.get_contract_instance( contract_file_name, contract_address ) def work_order_submit(self, work_order_id, worker_id, requester_id, work_order_request, id=None): """ Submit work order request to the Ethereum block chain. Parameters: work_order_id Unique ID of the work order request worker_id Identifier for the worker requester_id Unique id to identify the requester work_order_request JSON RPC string work order request. Complete definition at work_order.py and defined in EEA specification 6.1.1. id Optional JSON RPC request ID Returns: 0 on success and non-zero on error. """ logging.info("Inside Ethereum work order submit\n") if (self.__contract_instance is not None): if not _is_valid_work_order_json(work_order_id, worker_id, requester_id, work_order_request): logging.error( "Invalid request string {}".format(work_order_request)) return ERROR txn_dict = self.__contract_instance.functions.workOrderSubmit( work_order_id, worker_id, requester_id, work_order_request).buildTransaction( self.__eth_client.get_transaction_params()) try: txn_receipt = self.__eth_client.execute_transaction(txn_dict) return SUCCESS except Exception as e: logging.error("Exception occurred when trying to execute " + "workOrderSubmit transaction on chain " + str(e)) return ERROR else: logging.error("Work order contract instance is not initialized") return ERROR def work_order_complete(self, work_order_id, work_order_response): """ This function is called by the Worker Service to complete a work order successfully or in error. This API is for the proxy model. Parameters: work_order_id Unique ID to identify the work order request work_order_response Work order response data in a string Returns: errorCode 0 on success or non-zero on error. """ if (self.__contract_instance is not None): txn_dict = self.__contract_instance.functions.workOrderComplete( work_order_id, work_order_response).buildTransaction( self.__eth_client.get_transaction_params()) try: txn_receipt = self.__eth_client.execute_transaction(txn_dict) return SUCCESS except Exception as e: logging.error("Execption occurred when trying to execute " + "workOrderComplete transaction on chain " + str(e)) return ERROR else: logging.error("Work order contract instance is not initialized") return ERROR def work_order_get_result(self, work_order_id, id=None): """ Query blockchain to get a work order result. This function starts an event handler for handling the workOrderCompleted event from the Ethereum blockchain. Parameters: work_order_id Work Order ID that was sent in the corresponding work_order_submit request id Optional JSON RPC request ID Returns: Tuple containing work order status, worker id, work order request, work order response, and error code. None on error. """ logging.info("About to start Ethereum event handler") # Start an event listener that listens for events from the proxy # blockchain, extracts response payload from there and passes it # on to the requestor w3 = BlockchainInterface(self._config) contract = self.__contract_instance_evt # Listening only for workOrderCompleted event now listener = w3.newListener(contract, "workOrderCompleted") try: daemon = EventProcessor(self._config) # Wait for the workOrderCompleted event after starting the # listener and handler event = asyncio.get_event_loop()\ .run_until_complete(daemon.get_event_synchronously( listener, is_wo_id_in_event, wo_id=work_order_id)) # Get the first element as this is a list of one event # obtained from gather() in ethereum_listener work_order_response = event[0]["args"]["workOrderResponse"] return json.loads(work_order_response) except KeyboardInterrupt: asyncio.get_event_loop().run_until_complete(daemon.stop()) def encryption_key_retrieve(self, worker_id, last_used_key_nonce, tag, requester_id, signature_nonce=None, signature=None, id=None): """ Get Encryption Key Request Payload. Not supported for Ethereum. """ pass def encryption_key_start(self, tag, id=None): """ Inform the Worker that it should start encryption key generation for this requester. Not supported for Ethereum. """ pass def encryption_key_set(self, worker_id, encryption_key, encryption_nonce, tag, signature, id=None): """ Set Encryption Key Request Payload. Not supported for Ethereum. """ pass def encryption_key_get(self, worker_id, requester_id, last_used_key_nonce=None, tag=None, signature_nonce=None, signature=None, id=None): """ Get Encryption Key Request Payload. Not supported for Ethereum. """ pass