Exemple #1
0
    def complete_access_service(did,
                                service_endpoint,
                                attributes,
                                template_id,
                                reward_contract_address=None,
                                service_type=ServiceTypes.ASSET_ACCESS):
        """
        Build the access service.

        :param did: DID, str
        :param service_endpoint: identifier of the service inside the asset DDO, str
        :param template_id: id of the template use to create the service, str
        :param reward_contract_address: hex str ethereum address of deployed reward condition
            smart contract
        :return: ServiceAgreement
        """
        param_map = {
            '_documentId': did_to_id(did),
            '_amount': attributes['main']['price']
        }

        if reward_contract_address is not None:
            param_map['_rewardAddress'] = reward_contract_address

        try:
            param_map['_did'] = did_to_id(did)
            param_map['_amounts'] = attributes['main']['_amounts']
            param_map['_receivers'] = attributes['main']['_receivers']
            param_map['_tokenAddress'] = attributes['main']['_tokenAddress']
            param_map['_numberNfts'] = attributes['main']['_numberNfts']
        except KeyError as e:
            logger.error(f'Error mapping field {e}')

        sla_template_dict = get_sla_template(service_type)
        sla_template = ServiceAgreementTemplate(template_id, service_type,
                                                attributes['main']['creator'],
                                                sla_template_dict)
        sla_template.template_id = template_id
        conditions = sla_template.conditions[:]

        for cond in conditions:
            for param in cond.parameters:
                param.value = param_map.get(param.name, '')

            if cond.timeout > 0:
                cond.timeout = attributes['main']['timeout']

        sla_template.set_conditions(conditions)
        sa = ServiceAgreement(attributes, sla_template, service_endpoint,
                              service_type)
        return sa
Exemple #2
0
    def is_access_granted(self, agreement_id, did, consumer_address):
        """
        Check permission for the agreement.

        Verify on-chain that the `consumer_address` has permission to access the given asset `did`
        according to the `agreement_id`.

        :param agreement_id: id of the agreement, hex str
        :param did: DID, str
        :param consumer_address: ethereum account address of consumer, hex str
        :return: bool True if user has permission
        """
        agreement_consumer = self._keeper.access_template.get_agreement_consumer(
            agreement_id)

        if agreement_consumer is None:
            return False

        if agreement_consumer != consumer_address:
            logger.warning(f'Invalid consumer address {consumer_address} and/or '
                           f'service agreement id {agreement_id} (did {did})'
                           f', agreement consumer is {agreement_consumer}')
            return False

        document_id = did_to_id(did)
        return self._keeper.access_condition.check_permissions(
            document_id, consumer_address
        )
Exemple #3
0
    def owner(self, did):
        """
        Return the owner of the asset.

        :param did: DID, str
        :return: the ethereum address of the owner/publisher of given asset did, hex-str
        """
        # return self._get_metadata_provider(self._metadata_url).get_asset_ddo(did).proof['creator']
        return self._keeper.did_registry.get_did_owner(did_to_id(did))
Exemple #4
0
    def get_permissions(self, did, address):
        """
        Gets access permission of a grantee

        :param did: the id of an asset on-chain, hex str
        :param address: ethereum account address, hex str
        :return: true if the address has access permission to a DID
        """
        asset_id = add_0x_prefix(did_to_id(did))
        return self._keeper.did_registry.get_permission(asset_id, address)
Exemple #5
0
def refund_reward(event, agreement_id, did, service_agreement, price,
                  consumer_account, publisher_address, condition_ids,
                  escrow_condition_id):
    """
    Refund the reward to the publisher address.

    :param event: AttributeDict with the event data.
    :param agreement_id: id of the agreement, hex str
    :param did: DID, str
    :param service_agreement: ServiceAgreement instance
    :param price: Asset price, int
    :param consumer_account: Account instance of the consumer
    :param publisher_address: ethereum account address of publisher, hex str
    :param condition_ids: is a list of bytes32 content-addressed Condition IDs, bytes32
    :param escrow_condition_id: hex str the id of escrow reward condition at this `agreement_id`
    """
    logger.debug(
        f"trigger refund (agreement {agreement_id}) after event {event}.")
    if Keeper.get_instance().condition_manager.get_condition_state(
            escrow_condition_id) > 1:
        logger.debug(
            f'escrow payment condition already fulfilled/aborted: '
            f'agreementId={agreement_id}, escrow reward conditionId={escrow_condition_id},'
            f' publisher={publisher_address}')
        return

    access_id, lock_id = condition_ids[:2]
    name_to_parameter = {
        param.name: param
        for param in
        service_agreement.condition_by_name['escrowPayment'].parameters
    }
    document_id = add_0x_prefix(name_to_parameter['_documentId'].value)
    asset_id = add_0x_prefix(did_to_id(did))
    did_owner = Keeper.get_instance(
    ).agreement_manager.get_agreement_did_owner(agreement_id)
    assert document_id == asset_id, f'document_id {document_id} <=> asset_id {asset_id} mismatch.'
    assert price == service_agreement.get_price(), 'price mismatch.'
    try:
        escrow_condition = Keeper.get_instance().escrow_payment_condition
        tx_hash = escrow_condition.fulfill(
            agreement_id, price,
            Web3Provider.get_web3().toChecksumAddress(did_owner),
            consumer_account.address, lock_id, access_id, consumer_account)
        process_tx_receipt(
            tx_hash,
            getattr(escrow_condition.contract.events,
                    escrow_condition.FULFILLED_EVENT)(),
            'EscrowReward.Fulfilled')
    except Exception as e:
        logger.error(
            f'Error when doing escrow_payment_condition.fulfills (agreementId {agreement_id}): {e}',
            exc_info=1)
        raise e
Exemple #6
0
    def revoke_permissions(self, did, address_to_revoke, account):
        """
        Revoke access permission to an address.

        :param did: the id of an asset on-chain, hex str
        :param address_to_revoke: ethereum account address, hex str
        :param account: Account executing the action
        :return: bool
        """
        asset_id = add_0x_prefix(did_to_id(did))
        return self._keeper.did_registry.revoke_permission(asset_id, address_to_revoke, account)
Exemple #7
0
    def delegate_persmission(self, did, address_to_grant, account):
        """
        Grant access permission to an address.

        :param did: the id of an asset on-chain, hex str
        :param address_to_grant: ethereum account address, hex str
        :param account: Account executing the action
        :return: bool
        """
        asset_id = add_0x_prefix(did_to_id(did))
        return self._keeper.did_registry.grant_permission(asset_id, address_to_grant, account)
Exemple #8
0
    def transfer_nft(self, did, address, amount, account):
        """
        Transfer nft to another address. Return true if successful

        :param did: the id of an asset on-chain, hex str
        :param address: ethereum account address, hex str
        :param amount: amount of nft to be transfer, int
        :param account: Account executing the action

        """
        return self._keeper.did_registry.transfer_nft(did_to_id(did), address,
                                                      amount, account)
Exemple #9
0
    def transfer_ownership(self, did, new_owner_address, account):
        """
        Transfer did ownership to an address.

        :param did: the id of an asset on-chain, hex str
        :param new_owner_address: ethereum account address, hex str
        :param account: Account executing the action
        :return: bool
        """
        asset_id = add_0x_prefix(did_to_id(did))
        return self._keeper.did_registry.transfer_did_ownership(
            asset_id, new_owner_address, account)
Exemple #10
0
def test_did_to_id():
    did = DID.did("0x123")
    _id = did_to_id(did)
    assert _id is not None and len(_id) == 64, ''

    test_id = '%s' % secrets.token_hex(32)
    assert did_to_id(f'{NEVERMINED_PREFIX}{test_id}') == test_id
    assert did_to_id('did:nv1:011') == '011'
    assert did_to_id('did:nv:0') == '0'
    with pytest.raises(ValueError):
        did_to_id(NEVERMINED_PREFIX)

    assert did_to_id(f'{NEVERMINED_PREFIX}AB*&$#') == 'AB', ''
Exemple #11
0
def was_compute_triggered(agreement_id, did, compute_consumer_address, keeper):
    agreement_consumer = keeper.escrow_compute_execution_template.get_agreement_consumer(
        agreement_id)
    if agreement_consumer is None:
        return False

    if agreement_consumer != compute_consumer_address:
        logger.warning(
            f'Invalid consumer address {compute_consumer_address} and/or '
            f'service agreement id {agreement_id} (did {did})'
            f', agreement consumer is {agreement_consumer}')
        return False

    document_id = did_to_id(did)
    return keeper.compute_execution_condition.was_compute_triggered(
        document_id, compute_consumer_address)
Exemple #12
0
    def complete_compute_service(did, service_endpoint, attributes,
                                 template_id, reward_contract_address):
        """
        Build the access service.

        :param did: DID, str
        :param service_endpoint: identifier of the service inside the asset DDO, str
        :param template_id: id of the template use to create the service, str
        :param reward_contract_address: hex str ethereum address of deployed reward condition
            smart contract
        :return: ServiceAgreement
        """
        param_map = {
            '_documentId': did_to_id(did),
            '_amount': attributes['main']['price'],
            '_rewardAddress': reward_contract_address
        }

        try:
            param_map['_amounts'] = attributes['main']['_amounts']
            param_map['_receivers'] = attributes['main']['_receivers']
        except KeyError:
            pass

        sla_template_dict = get_sla_template(ServiceTypes.CLOUD_COMPUTE)
        sla_template = ServiceAgreementTemplate(template_id,
                                                ServiceTypes.CLOUD_COMPUTE,
                                                attributes['main']['creator'],
                                                sla_template_dict)
        sla_template.template_id = template_id
        conditions = sla_template.conditions[:]
        for cond in conditions:
            for param in cond.parameters:
                param.value = param_map.get(param.name, '')

            if cond.timeout > 0:
                cond.timeout = attributes['main']['timeout']

        sla_template.set_conditions(conditions)
        sa = ServiceAgreement(attributes, sla_template, service_endpoint,
                              ServiceTypes.CLOUD_COMPUTE)
        return sa
Exemple #13
0
def fulfill_exec_compute_condition(event, agreement_id, did, service_agreement,
                                   consumer_address, publisher_account, exec_compute_condition_id):
    """
    Fulfill the exec compute condition.

    :param event: AttributeDict with the event data.
    :param agreement_id: id of the agreement, hex str
    :param did: DID, str
    :param service_agreement: ServiceAgreement instance
    :param consumer_address: ethereum account address of consumer, hex str
    :param publisher_account: Account instance of the publisher
    :param exec_compute_condition_id: hex str the id of the exec compute condition for this
        `agreement_id`
    """
    if not event:
        logger.debug(f'`fulfill_exec_compute_condition` got empty event: '
                     f'event listener timed out.')
        return

    keeper = Keeper.get_instance()
    if keeper.condition_manager.get_condition_state(exec_compute_condition_id) > 1:
        logger.debug(
            f'exec compute condition already fulfilled/aborted: '
            f'agreementId={agreement_id}, exec compute conditionId={exec_compute_condition_id}'
        )
        return

    logger.debug(f"grant access (agreement {agreement_id}) after event {event}.")
    name_to_parameter = {param.name: param for param in
                         service_agreement.condition_by_name['execCompute'].parameters}
    document_id = add_0x_prefix(name_to_parameter['_documentId'].value)
    asset_id = add_0x_prefix(did_to_id(did))
    assert document_id == asset_id, f'document_id {document_id} <=> asset_id {asset_id} mismatch.'

    args = (
        agreement_id,
        document_id,
        consumer_address,
        publisher_account
    )
    process_fulfill_condition(args, keeper.compute_execution_condition, exec_compute_condition_id,
                              logger, keeper, 10)
Exemple #14
0
def is_access_granted(agreement_id, did, consumer_address, keeper):
    agreement_consumer = keeper.access_template.get_agreement_consumer(
        agreement_id)
    logger.info(agreement_consumer)

    if agreement_consumer is None:
        return False

    if agreement_consumer != consumer_address:
        logger.warning(f'Invalid consumer address {consumer_address} and/or '
                       f'service agreement id {agreement_id} (did {did})'
                       f', agreement consumer is {agreement_consumer}')
        return False

    document_id = did_to_id(did)

    is_granted = keeper.access_condition.check_permissions(
        document_id, consumer_address)
    logger.info(is_granted)
    return is_granted
Exemple #15
0
 def grant_access(event, instance, agr_id, did, cons_address, account):
     instance.agreements.conditions.grant_access(
         agr_id, add_0x_prefix(did_to_id(did)), cons_address, account)
Exemple #16
0
def is_owner_granted(did, consumer_address, keeper):
    document_id = did_to_id(did)
    is_granted = keeper.access_condition.check_permissions(
        document_id, consumer_address)
    logger.info(is_granted)
    return is_granted
Exemple #17
0
    def create(self,
               metadata,
               publisher_account,
               service_descriptors=None,
               providers=None,
               authorization_type=ServiceAuthorizationTypes.PSK_RSA,
               use_secret_store=False,
               activity_id=None,
               attributes=None,
               asset_rewards={
                   "_amounts": [],
                   "_receivers": []
               },
               cap=None,
               royalties=None):
        """
        Register an asset in both the keeper's DIDRegistry (on-chain) and in the Metadata store.

        :param metadata: dict conforming to the Metadata accepted by Nevermined Protocol.
        :param publisher_account: Account of the publisher registering this asset
        :param service_descriptors: list of ServiceDescriptor tuples of length 2.
            The first item must be one of ServiceTypes and the second
            item is a dict of parameters and values required by the service
        :param providers: list of addresses of providers of this asset (a provider is
            an ethereum account that is authorized to provide asset services)
        :param authorization_type: str indicate the authorization type that is going to be used
        to encrypt the urls.
            SecretStore, PSK-RSA and PSK-ECDSA are supported.
        :param use_secret_store: bool indicate whether to use the secret store directly for
            encrypting urls (Uses Gateway provider service if set to False)
        :param activity_id: identifier of the activity creating the new entity
        :param attributes: attributes associated with the action
        :param asset_rewards: rewards distribution including the amounts and the receivers
        :param cap: max cap of nfts that can be minted for the asset
        :param royalties: royalties in the secondary market going to the original creator
        :return: DDO instance
        """
        assert isinstance(
            metadata,
            dict), f'Expected metadata of type dict, got {type(metadata)}'

        # copy metadata so we don't change the original
        metadata_copy = copy.deepcopy(metadata)

        # Create a DDO object
        ddo = DDO()
        gateway = GatewayProvider.get_gateway()
        ddo_service_endpoint = self._get_metadata_provider(
        ).get_service_endpoint()
        metadata_service_desc = ServiceDescriptor.metadata_service_descriptor(
            metadata_copy, ddo_service_endpoint)
        if metadata_copy['main']['type'] == 'dataset' or metadata_copy['main'][
                'type'] == 'algorithm':
            access_service_attributes = self._build_access(
                metadata_copy, publisher_account, asset_rewards)
            if not service_descriptors:
                if authorization_type == ServiceAuthorizationTypes.PSK_RSA:
                    service_descriptors = [
                        ServiceDescriptor.authorization_service_descriptor(
                            self._build_authorization(
                                authorization_type,
                                public_key=gateway.get_rsa_public_key(
                                    self._config)),
                            gateway.get_access_endpoint(self._config))
                    ]
                elif authorization_type == ServiceAuthorizationTypes.PSK_ECDSA:
                    service_descriptors = [
                        ServiceDescriptor.authorization_service_descriptor(
                            self._build_authorization(
                                authorization_type,
                                public_key=gateway.get_ecdsa_public_key(
                                    self._config)),
                            gateway.get_access_endpoint(self._config))
                    ]
                else:
                    service_descriptors = [
                        ServiceDescriptor.authorization_service_descriptor(
                            self._build_authorization(authorization_type,
                                                      threshold=0),
                            self._config.secret_store_url)
                    ]
                service_descriptors += [
                    ServiceDescriptor.access_service_descriptor(
                        access_service_attributes,
                        gateway.get_access_endpoint(self._config))
                ]
            else:
                service_types = set(map(lambda x: x[0], service_descriptors))
                if ServiceTypes.AUTHORIZATION not in service_types:
                    if authorization_type == ServiceAuthorizationTypes.PSK_RSA:
                        service_descriptors += [
                            ServiceDescriptor.authorization_service_descriptor(
                                self._build_authorization(
                                    authorization_type,
                                    public_key=gateway.get_rsa_public_key(
                                        self._config)),
                                gateway.get_access_endpoint(self._config))
                        ]
                    elif authorization_type == ServiceAuthorizationTypes.PSK_ECDSA:
                        service_descriptors += [
                            ServiceDescriptor.authorization_service_descriptor(
                                self._build_authorization(
                                    authorization_type,
                                    public_key=gateway.get_ecdsa_public_key(
                                        self._config)),
                                gateway.get_access_endpoint(self._config))
                        ]
                    else:
                        service_descriptors += [
                            ServiceDescriptor.authorization_service_descriptor(
                                self._build_authorization(authorization_type,
                                                          threshold=0),
                                self._config.secret_store_url)
                        ]
                else:
                    service_descriptors += [
                        ServiceDescriptor.access_service_descriptor(
                            access_service_attributes,
                            gateway.get_access_endpoint(self._config))
                    ]
        else:
            if not service_descriptors:
                service_descriptors = []
            else:
                service_descriptors += []
            logger.info('registering a workflow.')
        # Add all services to ddo
        service_descriptors = [metadata_service_desc] + service_descriptors

        services = ServiceFactory.build_services(service_descriptors)
        checksums = dict()
        for service in services:
            checksums[str(service.index)] = checksum(service.main)

        # Adding proof to the ddo.
        ddo.add_proof(checksums, publisher_account)

        # Generating the did and adding to the ddo.
        did = ddo.assign_did(DID.did(ddo.proof['checksum']))
        logger.debug(f'Generating new did: {did}')
        # Check if it's already registered first!
        if did in self._get_metadata_provider().list_assets():
            raise DIDAlreadyExist(
                f'Asset id {did} is already registered to another asset.')

        for service in services:
            if service.type == ServiceTypes.ASSET_ACCESS:
                access_service = ServiceFactory.complete_access_service(
                    did, gateway.get_access_endpoint(self._config),
                    access_service_attributes,
                    self._keeper.access_template.address,
                    self._keeper.escrow_payment_condition.address)
                ddo.add_service(access_service)
            elif service.type == ServiceTypes.METADATA:
                ddo_service_endpoint = service.service_endpoint.replace(
                    '{did}', did)
                service.set_service_endpoint(ddo_service_endpoint)
                ddo.add_service(service)
            elif service.type == ServiceTypes.CLOUD_COMPUTE:
                compute_service = ServiceFactory.complete_compute_service(
                    did, service.service_endpoint, service.attributes,
                    self._keeper.compute_execution_condition.address,
                    self._keeper.escrow_payment_condition.address)
                ddo.add_service(compute_service)
            else:
                ddo.add_service(service)

        ddo.proof['signatureValue'] = self._keeper.sign_hash(
            add_ethereum_prefix_and_hash_msg(did_to_id_bytes(did)),
            publisher_account)

        # Add public key and authentication
        ddo.add_public_key(did, publisher_account.address)

        ddo.add_authentication(did, PUBLIC_KEY_TYPE_RSA)

        # Setup metadata service
        # First compute files_encrypted
        if metadata_copy['main']['type'] in ['dataset', 'algorithm']:
            assert metadata_copy['main'][
                'files'], 'files is required in the metadata main attributes.'
            logger.debug('Encrypting content urls in the metadata.')
            if not use_secret_store:
                encrypt_endpoint = gateway.get_encrypt_endpoint(self._config)
                files_encrypted = gateway.encrypt_files_dict(
                    metadata_copy['main']['files'], encrypt_endpoint,
                    ddo.asset_id, authorization_type)
            else:
                files_encrypted = self._get_secret_store(publisher_account) \
                    .encrypt_document(
                    did_to_id(did),
                    json.dumps(metadata_copy['main']['files']),
                )
            # only assign if the encryption worked
            if files_encrypted:
                logger.debug(
                    f'Content urls encrypted successfully {files_encrypted}')
                index = 0
                for file in metadata_copy['main']['files']:
                    file['index'] = index
                    index = index + 1
                    del file['url']
                metadata_copy['encryptedFiles'] = files_encrypted
            else:
                raise AssertionError('Encrypting the files failed.')

        # DDO url and `Metadata` service

        logger.debug(f'Generated ddo and services, DID is {ddo.did},'
                     f' metadata service @{ddo_service_endpoint}.')
        response = None

        # register on-chain
        registered_on_chain = self._keeper.did_registry.register(
            ddo.asset_id,
            checksum=Web3Provider.get_web3().toBytes(hexstr=ddo.asset_id),
            url=ddo_service_endpoint,
            account=publisher_account,
            cap=cap,
            royalties=royalties,
            providers=providers,
            activity_id=activity_id,
            attributes=attributes)
        if registered_on_chain is False:
            logger.warning(f'Registering {did} on-chain failed.')
            return None
        logger.info(f'Successfully registered DDO (DID={did}) on chain.')
        try:
            # publish the new ddo
            response = self._get_metadata_provider().publish_asset_ddo(ddo)
            logger.info('Asset/ddo published successfully in Metadata.')
        except ValueError as ve:
            raise ValueError(
                f'Invalid value to publish in the metadata: {str(ve)}')
        except Exception as e:
            logger.error(f'Publish asset in Metadata failed: {str(e)}')
        if not response:
            return None
        return ddo
Exemple #18
0
 def asset_id(self):
     """The asset id part of the DID"""
     if not self._did:
         return None
     return add_0x_prefix(did_to_id(self._did))
Exemple #19
0
def test_did():
    assert DID.did("0x123").startswith(NEVERMINED_PREFIX)
    assert len(DID.did("0x123")) - len(NEVERMINED_PREFIX) == 64
    _id = did_to_id(DID.did("0x123"))
    assert not _id.startswith(
        '0x'), 'id portion of did should not have a 0x prefix.'