diff --git a/examples/compute_example.py b/examples/compute_example.py index 0c7ab99..5d1fe46 100644 --- a/examples/compute_example.py +++ b/examples/compute_example.py @@ -59,6 +59,11 @@ def compute_example(verbose=False): keeper = Keeper.get_instance() provider = "0x068Ed00cF0441e4829D9784fCBe7b9e26D4BD8d0" + asset_rewards = { + "_amounts": ["10", "2"], + "_receivers": ["0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e", "0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0"] + } + # Setup accounts acc = Account( Web3.toChecksumAddress(PROVIDER_ADDRESS), PROVIDER_PASSWORD, PROVIDER_KEYFILE @@ -70,7 +75,7 @@ def compute_example(verbose=False): # Publish compute example_metadata.compute_ddo["main"]["dateCreated"] = next(date_created) ddo_compute = nevermined.assets.create_compute( - example_metadata.metadata, provider_acc, providers=[provider] + example_metadata.metadata, provider_acc, asset_rewards, providers=[provider] ) assert ddo_compute is not None, "Creating compute asset on-chain failed." print( diff --git a/nevermined_sdk_py/nevermined/assets.py b/nevermined_sdk_py/nevermined/assets.py index 6d05a42..09c2335 100644 --- a/nevermined_sdk_py/nevermined/assets.py +++ b/nevermined_sdk_py/nevermined/assets.py @@ -54,7 +54,7 @@ def _get_secret_store(self, account): 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): + activity_id=None, attributes=None, asset_rewards={"_amounts": [], "_receivers": []}): """ Register an asset in both the keeper's DIDRegistry (on-chain) and in the Metadata store. @@ -72,6 +72,7 @@ def create(self, metadata, publisher_account, 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 :return: DDO instance """ assert isinstance(metadata, dict), f'Expected metadata of type dict, got {type(metadata)}' @@ -87,7 +88,7 @@ def create(self, metadata, publisher_account, 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) + 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( @@ -259,7 +260,7 @@ def create(self, metadata, publisher_account, return None return ddo - def create_compute(self, metadata, publisher_account, + def create_compute(self, metadata, publisher_account, asset_rewards={"_amounts": [], "_receivers": []}, service_descriptors=None, providers=None, authorization_type=ServiceAuthorizationTypes.PSK_RSA, use_secret_store=False): """ @@ -268,6 +269,7 @@ def create_compute(self, metadata, publisher_account, :param metadata: dict conforming to the Metadata accepted by Nevermined Protocol. :param publisher_account: Account of the publisher registering this asset + :param asset_rewards: Asset rewards distribution including amounts and receivers :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 @@ -284,7 +286,7 @@ def create_compute(self, metadata, publisher_account, metadata_copy = copy.deepcopy(metadata) gateway = GatewayProvider.get_gateway() - compute_service_attributes = self._build_compute(metadata_copy, publisher_account) + compute_service_attributes = self._build_compute(metadata_copy, publisher_account, asset_rewards) service_descriptor = ServiceDescriptor.compute_service_descriptor( compute_service_attributes, gateway.get_execute_endpoint(self._config)) @@ -623,23 +625,27 @@ def _build_authorization(authorization_type, public_key=None, threshold=None): return authorization @staticmethod - def _build_access(metadata, publisher_account): + def _build_access(metadata, publisher_account, asset_rewards): return {"main": { "name": "dataAssetAccessServiceAgreement", "creator": publisher_account.address, "price": metadata[MetadataMain.KEY]['price'], "timeout": 3600, - "datePublished": metadata[MetadataMain.KEY]['dateCreated'] + "datePublished": metadata[MetadataMain.KEY]['dateCreated'], + "_amounts": asset_rewards["_amounts"], + "_receivers": asset_rewards["_receivers"] }} - def _build_compute(self, metadata, publisher_account): + def _build_compute(self, metadata, publisher_account, asset_rewards): return {"main": { "name": "dataAssetComputeServiceAgreement", "creator": publisher_account.address, "datePublished": metadata[MetadataMain.KEY]['dateCreated'], "price": metadata[MetadataMain.KEY]['price'], "timeout": 86400, - "provider": self._build_provider_config() + "provider": self._build_provider_config(), + "_amounts": asset_rewards["_amounts"], + "_receivers": asset_rewards["_receivers"] } } diff --git a/nevermined_sdk_py/nevermined/conditions.py b/nevermined_sdk_py/nevermined/conditions.py index 5a950f6..166f7e7 100644 --- a/nevermined_sdk_py/nevermined/conditions.py +++ b/nevermined_sdk_py/nevermined/conditions.py @@ -1,6 +1,6 @@ -from eth_utils import add_0x_prefix -from contracts_lib_py.web3_provider import Web3Provider from common_utils_py.did import did_to_id +from common_utils_py.utils.utilities import to_checksum_addresses +from eth_utils import add_0x_prefix class Conditions: @@ -42,13 +42,14 @@ def grant_access(self, agreement_id, did, grantee_address, account): receipt = self._keeper.access_secret_store_condition.get_tx_receipt(tx_hash) return bool(receipt and receipt.status == 1) - def release_reward(self, agreement_id, amount, account): + def release_reward(self, agreement_id, amounts, receivers, account): """ Release reward condition. :param agreement_id: id of the agreement, hex str - :param amount: Amount of tokens, int - :param account: Account + :param amounts: Amounts of tokens, int[] + :param receivers: Token receivers, str[] + :param account sending the transaction :return: """ agreement_values = self._keeper.agreement_manager.get_agreement(agreement_id) @@ -58,9 +59,9 @@ def release_reward(self, agreement_id, amount, account): access_id, lock_id = agreement_values.condition_ids[:2] tx_hash = self._keeper.escrow_reward_condition.fulfill( agreement_id, - amount, - Web3Provider.get_web3().toChecksumAddress(owner), - consumer, + amounts, + to_checksum_addresses(receivers), + owner, lock_id, access_id, account @@ -68,13 +69,14 @@ def release_reward(self, agreement_id, amount, account): receipt = self._keeper.escrow_reward_condition.get_tx_receipt(tx_hash) return bool(receipt and receipt.status == 1) - def refund_reward(self, agreement_id, amount, account): + def refund_reward(self, agreement_id, amounts, receivers, account): """ Refund reaward condition. :param agreement_id: id of the agreement, hex str - :param amount: Amount of tokens, int - :param account: Account + :param amounts: Amounts of tokens, int[] + :param receivers: Token receivers, str[] + :param account sending the transaction :return: """ - return self.release_reward(agreement_id, amount, account) + return self.release_reward(agreement_id, amounts, receivers, account) diff --git a/setup.py b/setup.py index 2f6ed9c..1139cbe 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ 'pyopenssl', 'PyJWT', # not jwt 'PyYAML==4.2b4', - 'common-utils-py==0.4.2', - 'contracts-lib-py==0.5.3', + 'common-utils-py==0.4.6', + 'contracts-lib-py==0.5.5', 'nevermined-secret-store==0.1.0', 'requests~=2.21.0', 'deprecated', @@ -65,7 +65,7 @@ author="nevermined-io", author_email='root@nevermined.io', classifiers=[ - 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Natural Language :: English', @@ -89,6 +89,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/nevermined-io/sdk-py', - version='0.7.0', + version='0.8.0', zip_safe=False, ) diff --git a/tests/conftest.py b/tests/conftest.py index d8de200..f66fd81 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,6 +6,7 @@ from common_utils_py.agreements.service_types import ServiceTypes from common_utils_py.did import DID from common_utils_py.metadata.metadata import Metadata +from common_utils_py.utils.utilities import generate_prefixed_id from contracts_lib_py.contract_handler import ContractHandler from contracts_lib_py.web3_provider import Web3Provider @@ -149,6 +150,8 @@ def setup_agreements_enviroment(ddo_sample): keeper = Keeper.get_instance() ddo = ddo_sample + ddo._did = DID.did({"0": generate_prefixed_id()}) + keeper.did_registry.register( ddo.asset_id, checksum=Web3Provider.get_web3().toBytes(hexstr=ddo.asset_id), diff --git a/tests/nevermined/test_agreements.py b/tests/nevermined/test_agreements.py index 5577d9b..077c4cd 100644 --- a/tests/nevermined/test_agreements.py +++ b/tests/nevermined/test_agreements.py @@ -1,5 +1,6 @@ from common_utils_py.agreements.service_agreement import ServiceAgreement from common_utils_py.agreements.service_types import ServiceTypes, ServiceTypesIndices +from common_utils_py.utils.utilities import to_checksum_addresses from nevermined_sdk_py.gateway.gateway import Gateway from nevermined_sdk_py.nevermined.keeper import NeverminedKeeper as Keeper @@ -70,9 +71,12 @@ def test_sign_agreement(publisher_instance, consumer_instance, registered_ddo): assert event, 'no event for AccessSecretStoreCondition.Fulfilled' # Fulfill escrow_reward_condition + amounts = list(map(int, service_agreement.get_param_value_by_name('_amounts'))) + receivers = to_checksum_addresses(service_agreement.get_param_value_by_name('_receivers')) + tx_hash = keeper.escrow_reward_condition.fulfill( - agreement_id, price, publisher_acc.address, - consumer_acc.address, lock_cond_id, + agreement_id, amounts, receivers, + publisher_acc.address, lock_cond_id, access_cond_id, publisher_acc ) keeper.escrow_reward_condition.get_tx_receipt(tx_hash) @@ -161,9 +165,13 @@ def test_agreement_status(setup_agreements_enviroment, agreements): "escrowReward": 1 } } + + amounts = list(map(int, service_agreement.get_param_value_by_name('_amounts'))) + receivers = to_checksum_addresses(service_agreement.get_param_value_by_name('_receivers')) + tx_hash = keeper.escrow_reward_condition.fulfill( - agreement_id, price, publisher_acc.address, - consumer_acc.address, lock_cond_id, + agreement_id, amounts, to_checksum_addresses(receivers), + publisher_acc.address, lock_cond_id, access_cond_id, publisher_acc ) keeper.escrow_reward_condition.get_tx_receipt(tx_hash) diff --git a/tests/nevermined/test_assets.py b/tests/nevermined/test_assets.py index 7f2f753..8b2d57a 100644 --- a/tests/nevermined/test_assets.py +++ b/tests/nevermined/test_assets.py @@ -264,7 +264,11 @@ def test_assets_workflow(publisher_instance, workflow_ddo): def test_assets_compute(publisher_instance, metadata): publisher = publisher_instance.main_account - ddo = publisher_instance.assets.create_compute(metadata, publisher) + asset_rewards = { + "_amounts": ["10", "2"], + "_receivers": ["0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e", "0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0"] + } + ddo = publisher_instance.assets.create_compute(metadata, publisher, asset_rewards=asset_rewards) assert ddo publisher_instance.assets.retire(ddo.did) diff --git a/tests/nevermined/test_buy_asset.py b/tests/nevermined/test_buy_asset.py index a5c0975..27c7c8a 100644 --- a/tests/nevermined/test_buy_asset.py +++ b/tests/nevermined/test_buy_asset.py @@ -42,6 +42,7 @@ def test_buy_asset(publisher_instance_no_init, consumer_instance_no_init): sa = ServiceAgreement.from_service_dict(service.as_dictionary()) # This will send the access request to Gateway which in turn will execute the agreement on-chain consumer_instance_no_init.accounts.request_tokens(consumer_account, 100) + agreement_id = consumer_instance_no_init.assets.order( ddo.did, sa.index, consumer_account, consumer_account) @@ -68,8 +69,11 @@ def test_buy_asset(publisher_instance_no_init, consumer_instance_no_init): assert event, 'no event for AccessSecretStoreCondition.Fulfilled' assert consumer_instance_no_init.agreements.is_access_granted(agreement_id, ddo.did, consumer_account.address) + amounts = list(map(int, service.get_param_value_by_name('_amounts'))) + receivers = service.get_param_value_by_name('_receivers') + publisher_instance_no_init.agreements.conditions.release_reward( - agreement_id, sa.get_price(), pub_acc) + agreement_id, amounts, receivers, pub_acc) assert consumer_instance_no_init.assets.access( agreement_id, diff --git a/tests/resources/helper_functions.py b/tests/resources/helper_functions.py index 89c71ff..ae192ee 100644 --- a/tests/resources/helper_functions.py +++ b/tests/resources/helper_functions.py @@ -126,8 +126,12 @@ def get_computing_ddo(): def get_registered_ddo(nevermined_instance, account): metadata = get_metadata() + asset_rewards = { + "_amounts": ["10", "2"], + "_receivers": ["0x00Bd138aBD70e2F00903268F3Db08f2D25677C9e", "0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0"] + } metadata['main']['files'][0]['checksum'] = str(uuid.uuid4()) - ddo = nevermined_instance.assets.create(metadata, account) + ddo = nevermined_instance.assets.create(metadata, account, asset_rewards=asset_rewards) return ddo