Beispiel #1
0
    def get_provenance_entry(self, provenance_id):
        """
        Fetch from the on-chain Provenance registry the information about one provenance event, given a provenance id

        :param provenance_id: provenance ID
        :return: information on-chain related with the provenance
        """
        provenance_entry = self._keeper.did_registry.get_provenance_entry(
            convert_to_bytes(provenance_id))
        provenance_entry['did'] = id_to_did(provenance_entry['did'])
        provenance_entry['related_did'] = id_to_did(
            provenance_entry['related_did'])
        return provenance_entry
Beispiel #2
0
    def validate_execute(self, agreement_id, workflow_did, consumer_address):
        keeper = keeper_instance()

        asset_id = keeper.agreement_manager.get_agreement(agreement_id).did
        did = id_to_did(asset_id)
        asset = DIDResolver(keeper.did_registry).resolve(did)

        if not was_compute_triggered(agreement_id, did, consumer_address, keeper):

            agreement = keeper.agreement_manager.get_agreement(agreement_id)
            cond_ids = agreement.condition_ids
            self.check_ddo(did, agreement_id, asset_id, consumer_address, keeper, cond_ids, ServiceTypes.CLOUD_COMPUTE)

            compute_condition_status = keeper.condition_manager.get_condition_state(cond_ids[0])
            lock_condition_status = keeper.condition_manager.get_condition_state(cond_ids[1])
            escrow_condition_status = keeper.condition_manager.get_condition_state(
                cond_ids[2])
            logger.debug('ComputeExecutionCondition: %d' % compute_condition_status)
            logger.debug('LockPaymentCondition: %d' % lock_condition_status)
            logger.debug('EscrowPaymentCondition: %d' % escrow_condition_status)

            if lock_condition_status != ConditionState.Fulfilled.value:
                logger.debug('ServiceAgreement %s was not paid. Forbidden' % agreement_id)
                raise InvalidClaimError(
                    f"ServiceAgreement {agreement_id} was not paid, LockPaymentCondition status is {lock_condition_status}")

            fulfill_compute_condition(keeper, agreement_id, cond_ids, asset_id, consumer_address,
                                      self.provider_account)
            fulfill_escrow_payment_condition(keeper, agreement_id, cond_ids, asset,
                                             self.provider_account,
                                             ServiceTypes.CLOUD_COMPUTE)

            iteration = 0
            access_granted = False
            while iteration < ConfigSections.PING_ITERATIONS:
                iteration = iteration + 1
                logger.debug('Checking if compute was granted. Iteration %d' % iteration)
                if not was_compute_triggered(agreement_id, did, consumer_address, keeper):
                    time.sleep(ConfigSections.PING_SLEEP / 1000)
                else:
                    access_granted = True
                    break

            if not access_granted:
                msg = (
                    'Scheduling the compute execution failed. Either consumer address does not '
                    'have permission to execute this workflow or consumer address and/or service '
                    'agreement id is invalid.')
                logger.warning(msg)
                raise InvalidClientError(msg)
Beispiel #3
0
def execute_compute_job():
    """Call the execution of a workflow.
    Method deprecated, it will be replaced by `/execute` in further versions
    swagger_from_file: docs/execute_compute_job.yml
    """
    data = request.args
    required_attributes = [
        'serviceAgreementId', 'consumerAddress', 'signature', 'workflowDID'
    ]
    msg, status = check_required_attributes(required_attributes, data,
                                            'consume')
    if msg:
        return msg, status

    if not (data.get('signature')):
        return f'`signature is required in the call to "consume".', 400

    try:
        agreement_id = data.get('serviceAgreementId')
        consumer_address = data.get('consumerAddress')
        asset_id = keeper_instance().agreement_manager.get_agreement(
            agreement_id).did
        did = id_to_did(asset_id)
        if not was_compute_triggered(agreement_id, did, consumer_address,
                                     keeper_instance()):
            msg = (
                'Checking if the compute was triggered failed. Either consumer address does not '
                'have permission to executre this workflow or consumer address and/or service '
                'agreement id is invalid.')
            logger.warning(msg)
            return msg, 401

        workflow = DIDResolver(keeper_instance().did_registry).resolve(
            data.get('workflowDID'))
        body = {
            "serviceAgreementId": agreement_id,
            "workflow": workflow.as_dictionary()
        }

        response = requests_session.post(
            get_config().compute_api_url +
            '/api/v1/nevermined-compute-api/init',
            data=json.dumps(body),
            headers={'content-type': 'application/json'})
        return jsonify({"workflowId": response.content.decode('utf-8')})
    except Exception as e:
        logger.error(f'Error- {str(e)}', exc_info=1)
        return f'Error : {str(e)}', 500
Beispiel #4
0
def test_id_to_did():
    test_id = '%s' % secrets.token_hex(32)
    valid_did_text = 'did:nv:{}'.format(test_id)
    assert id_to_did(test_id) == valid_did_text

    # accept hex string from Web3 py
    assert id_to_did(Web3.toHex(hexstr=test_id)) == valid_did_text

    # accepts binary value
    assert id_to_did(Web3.toBytes(hexstr=test_id)) == valid_did_text

    with pytest.raises(TypeError):
        id_to_did(None)

    with pytest.raises(TypeError):
        id_to_did({'bad': 'value'})

    assert id_to_did('') == 'did:nv:0'
Beispiel #5
0
    def _handle_agreement_created_event(self, event, *_):
        if not event or not event.args:
            return

        if self._account.address != event.args["_accessProvider"]:
            logger.debug(
                f'skip agreement event because it does not match my provider '
                f'address {self._account.address}, event provider '
                f'address is {event.args["_accessProvider"]}')
            return
        agreement_id = None
        try:
            agreement_id = self._web3.toHex(event.args["_agreementId"])
            ids = self.db.get_agreement_ids()
            if ids:
                # logger.info(f'got agreement ids: #{agreement_id}#, ##{ids}##, \nid in ids: {
                # agreement_id in ids}')
                if agreement_id in ids:
                    logger.debug(
                        f'handle_agreement_created: skipping service agreement {agreement_id} '
                        f'because it already been processed before.')
                    return

            logger.debug(
                f'Start handle_agreement_created (agreementId {agreement_id}): event_args='
                f'{event.args}')

            did = id_to_did(event.args["_did"])
            agreement = self._keeper.agreement_manager.get_agreement(
                agreement_id)
            unfulfilled_conditions = self._get_unfulfill_conditions(
                agreement.template_id)
            self.process_condition_events(agreement_id,
                                          unfulfilled_conditions,
                                          did,
                                          event.args['_accessConsumer'],
                                          event.blockNumber,
                                          new_agreement=True,
                                          template_id=agreement.template_id)

            logger.debug(
                f'handle_agreement_created()  (agreementId {agreement_id}) -- '
                f'done registering event listeners.')
        except Exception as e:
            logger.error(
                f'Error in handle_agreement_created (agreementId {agreement_id}): {e}',
                exc_info=1)
Beispiel #6
0
def execute(agreement_id):
    """Call the execution of a workflow.
    swagger_from_file: docs/execute.yml
    """

    consumer_address = current_token["client_id"]
    workflow_did = current_token["did"]
    agreement_id = current_token["sub"]

    try:
        keeper = keeper_instance()
        asset_id = keeper_instance().agreement_manager.get_agreement(
            agreement_id).did
        did = id_to_did(asset_id)

        signature = '0x00'

        workflow = DIDResolver(
            keeper_instance().did_registry).resolve(workflow_did)
        body = {
            "serviceAgreementId": agreement_id,
            "workflow": workflow.as_dictionary()
        }

        response = requests_session.post(
            get_config().compute_api_url +
            '/api/v1/nevermined-compute-api/init',
            data=json.dumps(body),
            headers={'content-type': 'application/json'})
        if response.status_code != 200:
            msg = f'The compute API was not able to create the workflow. {response.content}'
            logger.warning(msg)
            return msg, 401

        used_by(generate_random_id(), did, consumer_address, 'compute',
                signature, 'compute', provider_acc, keeper)
        return jsonify({"workflowId": response.content.decode('utf-8')})

    except Exception as e:
        logger.error(f'Error- {str(e)}', exc_info=1)
        return f'Error : {str(e)}', 500
Beispiel #7
0
def is_allowed_read_compute(agreement_id, execution_id, consumer_address, signature, has_bearer_token=False):
    keeper = keeper_instance()

    ## Access check
    if not has_bearer_token:
        if not verify_signature(keeper, consumer_address, signature, execution_id):
            msg = (f'Invalid signature {signature} for '
                f'consumerAddress {consumer_address} and executionId {execution_id}.')
            logger.error(msg)
            return msg, False

    asset_id = keeper.agreement_manager.get_agreement(agreement_id).did
    did = id_to_did(asset_id)

    if not was_compute_triggered(agreement_id, did, consumer_address, keeper):
        msg = (
            'Getting access failed. Either consumer address does not '
            'have permission to execute this agreement or consumer address and/or service '
            'agreement id is invalid.')
        logger.warning(msg)
        return msg, False

    return 'OK', True
Beispiel #8
0
def consume():
    """Allows download of asset data file.
    Method deprecated, it will be replaced by `/access` in further versions
    swagger_from_file: docs/consume.yml
    """
    data = request.args
    required_attributes = [
        'serviceAgreementId', 'consumerAddress', 'signature', 'index'
    ]
    msg, status = check_required_attributes(required_attributes, data,
                                            'consume')
    if msg:
        return msg, status

    try:
        keeper = keeper_instance()
        agreement_id = data.get('serviceAgreementId')
        consumer_address = data.get('consumerAddress')
        asset_id = keeper.agreement_manager.get_agreement(agreement_id).did
        did = id_to_did(asset_id)

        if not is_access_granted(agreement_id, did, consumer_address, keeper):
            msg = (
                'Checking access permissions failed. Either consumer address does not have '
                'permission to consume this asset or consumer address and/or service agreement '
                'id is invalid.')
            logger.warning(msg)
            return msg, 401

        asset = DIDResolver(keeper.did_registry).resolve(did)
        signature = data.get('signature')
        index = int(data.get('index'))

        if not verify_signature(keeper, consumer_address, signature,
                                agreement_id):
            msg = f'Invalid signature {signature} for ' \
                  f'publisherAddress {consumer_address} and documentId {agreement_id}.'
            raise ValueError(msg)

        file_attributes = asset.metadata['main']['files'][index]
        content_type = file_attributes.get('contentType', None)

        try:
            auth_method = asset.authorization.main['service']
        except Exception:
            auth_method = constants.ConfigSections.DEFAULT_DECRYPTION_METHOD

        if auth_method not in constants.ConfigSections.DECRYPTION_METHODS:
            msg = (
                'The Authorization Method defined in the DDO is not part of the available '
                'methods supported'
                'by the Gateway: ' + auth_method)
            logger.warning(msg)
            return msg, 400

        url = get_asset_url_at_index(index, asset, provider_acc, auth_method)
        return get_asset(request, requests_session, content_type, url,
                         app.config['CONFIG_FILE'])
    except (ValueError, Exception) as e:
        logger.error(f'Error- {str(e)}', exc_info=1)
        return f'Error : {str(e)}', 500
Beispiel #9
0
def nft_transfer():
    """Allows the provider transfer and release the rewards.
    swagger_from_file: docs/nft_transfer.yml
    """
    required_attributes = [
        'agreementId', 'nftHolder', 'nftReceiver', 'nftAmount'
    ]
    data = request.json

    msg, status = check_required_attributes(required_attributes, data,
                                            'nft-transfer')
    if msg:
        return msg, status

    agreement_id = data.get('agreementId')
    nft_holder_address = data.get('nftHolder')
    nft_receiver_address = data.get('nftReceiver')
    nft_amount = data.get('nftAmount')

    keeper = keeper_instance()
    agreement = keeper.agreement_manager.get_agreement(agreement_id)
    did = id_to_did(agreement.did)
    ddo = DIDResolver(keeper.did_registry).resolve(did)

    try:
        ServiceAgreement.from_ddo(ServiceTypes.NFT_SALES, ddo)
    except ValueError as e:
        logger.error('nft-sales service not found on ddo for %s', did)
        return str(e), 400

    (lock_payment_condition_id, nft_transfer_condition_id,
     escrow_payment_condition_id) = agreement.condition_ids

    if not is_nft_holder(keeper, agreement.did, nft_amount,
                         nft_holder_address):
        msg = f'Holder {nft_holder_address} does not have enough NFTs to transfer'
        logger.warning(msg)
        return msg, 406

    if not is_lock_payment_condition_fulfilled(lock_payment_condition_id,
                                               keeper):
        msg = f'lockPayment condition for agreement_id={agreement_id} is not fulfilled'
        logger.warning(msg)
        return msg, 402

    if not is_nft_transfer_approved(nft_holder_address,
                                    get_provider_account().address, keeper):
        msg = f'Gateway ({get_provider_account().address}) is not approved to transfer nfts from {nft_holder_address}'
        logger.warning(msg)
        return msg, 405

    # fulfill transferNFT condition
    if not is_nft_transfer_condition_fulfilled(nft_transfer_condition_id,
                                               keeper):
        logger.debug('NFTTransfer condition not fulfilled')
        result = fulfill_for_delegate_nft_transfer_condition(
            agreement_id, agreement.did,
            Web3.toChecksumAddress(nft_holder_address),
            Web3.toChecksumAddress(nft_receiver_address), nft_amount,
            lock_payment_condition_id, keeper)
        if result is False:
            msg = f'There was an error fulfilling the NFTTransfer condition for agreement_id={agreement_id}'
            logger.error(msg)
            return msg, 500

    # fulfill escrowPayment condition
    if not is_escrow_payment_condition_fulfilled(escrow_payment_condition_id,
                                                 keeper):
        logger.debug('EscrowPayment condition not fulfilled')
        result = fulfill_escrow_payment_condition(
            keeper,
            agreement_id, [
                nft_transfer_condition_id, lock_payment_condition_id,
                escrow_payment_condition_id
            ],
            ddo,
            get_provider_account(),
            service_type=ServiceTypes.NFT_SALES)
        if result is False:
            msg = f'There was an error fulfilling the EscrowPayment condition for agreement_id={agreement_id}'
            logger.error(msg)
            return msg, 500

    return 'success', 200