Skip to content

Commit

Permalink
implementing nft access flow
Browse files Browse the repository at this point in the history
  • Loading branch information
aaitor committed May 12, 2021
1 parent d27aa93 commit ba926dc
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 75 deletions.
Expand Up @@ -31,15 +31,15 @@ def refund_reward(event, agreement_id, did, service_agreement, price, consumer_a
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 reward condition already fulfilled/aborted: '
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['escrowReward'].parameters}
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)
Expand Down
Expand Up @@ -24,11 +24,11 @@ def fulfill_lock_payment_condition(event, agreement_id, price, consumer_account,

keeper = Keeper.get_instance()
if keeper.condition_manager.get_condition_state(lock_condition_id) > 1:
logger.debug(f'lock reward condition already fulfilled/aborted: '
f'agreementId={agreement_id}, lockReward conditionId={lock_condition_id}')
logger.debug(f'lock payment condition already fulfilled/aborted: '
f'agreementId={agreement_id}, lockPayment conditionId={lock_condition_id}')
return

logger.debug(f"about to lock reward (agreement {agreement_id}) after event {event}.")
logger.debug(f"about to lock payment (agreement {agreement_id}) after event {event}.")

approved = keeper.token.token_approve(
keeper.lock_payment_condition.address, price, consumer_account)
Expand Down
17 changes: 13 additions & 4 deletions nevermined_sdk_py/assets/asset_consumer.py
Expand Up @@ -13,7 +13,7 @@ class AssetConsumer:

@staticmethod
def access(service_agreement_id, service_index, ddo, consumer_account, destination,
gateway, secret_store, config, index=None):
gateway, secret_store, config, index=None, service_type=ServiceTypes.ASSET_ACCESS):
"""
Download asset data files or result files from a compute job.
Expand All @@ -26,10 +26,11 @@ def access(service_agreement_id, service_index, ddo, consumer_account, destinati
:param secret_store: SecretStore instance
:param config: Sdk configuration instance
:param index: Index of the document that is going to be downloaded, int
:param service_type: service type
:return: Asset folder path, str
"""
did = ddo.did
sa = ServiceAgreement.from_ddo(ServiceTypes.ASSET_ACCESS, ddo)
sa = ServiceAgreement.from_ddo(service_type, ddo)
consume_url = sa.service_endpoint
if not consume_url:
logger.error(
Expand All @@ -49,6 +50,12 @@ def access(service_agreement_id, service_index, ddo, consumer_account, destinati
assert index >= 0, logger.error('index has to be 0 or a positive integer.')
assert index < len(ddo.metadata['main']['files']), logger.error(
'index can not be bigger than the number of files')

if service_type is ServiceTypes.NFT_ACCESS:
uri = '/nft-access'
else:
uri = '/access'

if index is not None:
gateway.access_service(
did,
Expand All @@ -57,7 +64,8 @@ def access(service_agreement_id, service_index, ddo, consumer_account, destinati
consumer_account,
asset_folder,
config,
index
index,
uri
)
else:
for i, _file in enumerate(ddo.metadata['main']['files']):
Expand All @@ -68,7 +76,8 @@ def access(service_agreement_id, service_index, ddo, consumer_account, destinati
consumer_account,
asset_folder,
config,
i
i,
uri
)

return asset_folder
Expand Down
14 changes: 12 additions & 2 deletions nevermined_sdk_py/gateway/gateway.py
Expand Up @@ -95,10 +95,10 @@ def encrypt_files_dict(files_dict, encrypt_endpoint, asset_id, method):
return json.loads(response.text)['hash']

@staticmethod
def access_service(did, service_agreement_id, service_endpoint, account, destination_folder, config, index):
def access_service(did, service_agreement_id, service_endpoint, account, destination_folder, config, index, uri='/access'):
cache_key = Gateway._generate_cache_key(account.address, service_agreement_id, did)
if cache_key not in Gateway._tokens_cache:
grant_token = generate_access_grant_token(account, service_agreement_id, did)
grant_token = generate_access_grant_token(account, service_agreement_id, did, uri)
access_token = Gateway.fetch_token(grant_token, config)
Gateway._tokens_cache[cache_key] = access_token
else:
Expand Down Expand Up @@ -325,6 +325,16 @@ def get_access_endpoint(config):
"""
return f'{Gateway.get_gateway_url(config)}/services/access'

@staticmethod
def get_nft_access_endpoint(config):
"""
Return the endpoint to access the asset.
:param config:Config
:return: Url, str
"""
return f'{Gateway.get_gateway_url(config)}/services/nft-access'

@staticmethod
def get_download_endpoint(config):
"""
Expand Down
98 changes: 64 additions & 34 deletions nevermined_sdk_py/nevermined/agreements.py
Expand Up @@ -12,14 +12,25 @@
from contracts_lib_py.utils import add_ethereum_prefix_and_hash_msg
from contracts_lib_py.web3_provider import Web3Provider

from nevermined_sdk_py.agreement_events.accessSecretStore import consume_asset, refund_reward
from nevermined_sdk_py.agreement_events.computeExecution import execute_computation
from nevermined_sdk_py.agreement_events.escrowAccessSecretStoreTemplate import \
from nevermined_sdk_py.agreement_events.access_agreement import consume_asset, refund_reward
from nevermined_sdk_py.agreement_events.compute_agreement import execute_computation
from nevermined_sdk_py.agreement_events.payments import \
fulfillLockRewardCondition
from nevermined_sdk_py.nevermined.conditions import Conditions
from web3 import Web3

logger = logging.getLogger(__name__)

ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'


def check_token_address(keeper, token_address):
if token_address == '0x0' or token_address == ZERO_ADDRESS:
return ZERO_ADDRESS
elif not Web3.isAddress(token_address):
return keeper.token.address
return token_address


class Agreements:
"""Nevermined agreements class."""
Expand Down Expand Up @@ -71,66 +82,85 @@ def create(self, did, index, agreement_id, consumer_address, account):
assert account.address in self._keeper.accounts, \
f'Unrecognized account address {account.address}'

agreement_template_approved = self._keeper.template_manager.is_template_approved(
self._keeper.access_template.address)
agreement_exec_template_approved = self._keeper.template_manager.is_template_approved(
self._keeper.escrow_compute_execution_template.address)
if not agreement_template_approved:
msg = (f'The EscrowAccessSecretStoreTemplate contract at address '
f'{self._keeper.access_template.address} is not '
f'approved and cannot be used for creating service agreements.')
logger.warning(msg)
raise InvalidAgreementTemplate(msg)
if not agreement_exec_template_approved:
msg = (f'The EscroComputeExecutionTemplate contract at address '
f'{self._keeper.agreement_exec_template_approved.address} is not '
f'approved and cannot be used for creating service agreements.')
logger.warning(msg)
raise InvalidAgreementTemplate(msg)

payment_involved = True
asset = self._asset_resolver.resolve(did)
asset_id = asset.asset_id
service_agreement = asset.get_service_by_index(index)
if service_agreement.type == ServiceTypes.ASSET_ACCESS:
service = asset.get_service_by_index(index)
if service.type == ServiceTypes.ASSET_ACCESS:
agreement_template = self._keeper.access_template
elif service_agreement.type == ServiceTypes.CLOUD_COMPUTE:
template_address = self._keeper.access_template.address
elif service.type == ServiceTypes.CLOUD_COMPUTE:
agreement_template = self._keeper.escrow_compute_execution_template
template_address = self._keeper.escrow_compute_execution_template.address
elif service.type == ServiceTypes.NFT_SALES:
agreement_template = self._keeper.nft_sales_template
template_address = self._keeper.nft_sales_template.address
elif service.type == ServiceTypes.NFT_ACCESS:
payment_involved = False
agreement_template = self._keeper.nft_access_template
template_address = self._keeper.nft_access_template.address
else:
raise Exception('The agreement could not be created. Review the index of your service.')

if agreement_template.get_agreement_consumer(agreement_id) != '0x0000000000000000000000000000000000000000':
agreement_template_approved = self._keeper.template_manager.is_template_approved(template_address)
if not agreement_template_approved:
msg = (f'The Service Agreement Template contract at address '
f'{template_address} is not '
f'approved and cannot be used for creating service agreements.')
logger.warning(msg)
raise InvalidAgreementTemplate(msg)

if agreement_template.get_agreement_consumer(agreement_id) != ZERO_ADDRESS:
raise ServiceAgreementExists(
f'Service agreement {agreement_id} already exists, cannot reuse '
f'the same agreement id.')

service_agreement = ServiceAgreement.from_service_index(index, asset)
token_address = check_token_address(
self._keeper, service_agreement.get_param_value_by_name('_tokenAddress'))

publisher_address = Web3Provider.get_web3().toChecksumAddress(asset.publisher)
condition_ids = service_agreement.generate_agreement_condition_ids(
agreement_id, asset_id, consumer_address, self._keeper)
agreement_id, asset_id, consumer_address, self._keeper, token_address=token_address)

time_locks = service_agreement.conditions_timelocks
time_outs = service_agreement.conditions_timeouts
if service_agreement.get_price() > self._keeper.token.get_token_balance(consumer_address):

if payment_involved and service_agreement.get_price() > self._keeper.token.get_token_balance(consumer_address):
return Exception(
f'The consumer balance is '
f'{self._keeper.token.get_token_balance(consumer_address)}. '
f'This balance is lower that the asset price {service_agreement.get_price()}.')

if service.type == ServiceTypes.NFT_SALES:
conditions_ordered = [condition_ids[1], condition_ids[0], condition_ids[2]]
elif service.type == ServiceTypes.NFT_ACCESS:
conditions_ordered = [condition_ids[1], condition_ids[0]]
else:
conditions_ordered = condition_ids

success = agreement_template.create_agreement(
agreement_id,
asset_id,
condition_ids,
conditions_ordered,
time_locks,
time_outs,
consumer_address,
account
)

if success:
self.conditions.lock_payment(
agreement_id,
asset_id,
service_agreement.get_amounts_int(),
service_agreement.get_receivers(),
account)
return self._is_condition_fulfilled(agreement_id, 'lockReward')
if payment_involved:
self.conditions.lock_payment(
agreement_id,
asset_id,
service_agreement.get_amounts_int(),
service_agreement.get_receivers(),
token_address,
account)
return self._is_condition_fulfilled(agreement_id, 'lockReward')
return True
return False

def status(self, agreement_id):
"""
Expand Down

0 comments on commit ba926dc

Please sign in to comment.