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
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)
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
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'
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)
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
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
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
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