示例#1
0
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
示例#2
0
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