class AssetService(object): """The entry point for accessing the asset service.""" def __init__(self, config): keeper = Keeper(config.keeper_options) self.dt_factory = keeper.dt_factory self.asset_provider = keeper.asset_provider self.verifier = VerifierService(config) self.tracer = TracerService(config) self.config = config def generate_ddo(self, metadata, services, owner_address, child_dts=None, verify=True): """ Create an asset document and declare its services. :param metadata: refers to the asset metadata :param services: list of asset services :param owner_address: refers to the asset owner :param child_dts: list of child asset identifiers :param verify: check the correctness of asset services :return ddo: DDO instance """ ddo = DDO() ddo.add_metadata(metadata, child_dts) ddo.add_creator(owner_address) for service in services: ddo.add_service(service) ddo.assign_dt(DTHelper.generate_new_dt()) ddo.create_proof() # make sure the generated ddo is under system constraits if verify and not self.verifier.verify_services(ddo): raise AssertionError(f'Service agreements are not satisfied') return ddo def publish_dt(self, ddo, issuer_wallet): """ Publish a ddo to the decentralized storage network and register its data token on the smart-contract chain. :param ddo: refers to the asset DDO document :param issuer_wallet: issuer account, enterprize now :return """ ipfs_client = IPFSProvider(self.config) ipfs_path = ipfs_client.add(ddo.to_dict()) dt = DTHelper.dt_to_id(ddo.dt) owner = ddo.creator isLeaf = not bool(ddo.child_dts) checksum = ddo.proof['checksum'] self.dt_factory.mint_dt(dt, owner, isLeaf, checksum, ipfs_path, issuer_wallet) return def grant_dt_perm(self, dt, grantee, owner_wallet): """ Grant one dt to other dt. :param dt: refers to data token identifier :param grantee: refers to granted dt identifier :param owner_wallet: owner account :return """ _dt = DTHelper.dt_to_id(dt) _grantee = DTHelper.dt_to_id(grantee) self.dt_factory.grant_dt(_dt, _grantee, owner_wallet) return def activate_cdt(self, cdt, child_dts, aggregator_wallet): """ Activate cdt when all perms are ready. :param cdt: refers to cdt identifier :param child_dts: associated with child_dts identifier :param aggregator_wallet: aggregator account :return """ _cdt = DTHelper.dt_to_id(cdt) _child_dts = [DTHelper.dt_to_id(dt) for dt in child_dts] self.dt_factory.start_compose_dt(_cdt, _child_dts, aggregator_wallet) return def check_service_terms(self, cdt, dt, owner_address, signature): """ Check service agreements automatically when receiving a remote permission authorization request, used by Compute-to-Data. :param cdt: refers to cdt identifier provided by aggregator :param dt: refers to dt identifier owned by the provider grid :param owner_address: asset owner address :param signature: signed by aggregator, [consume_address, cdt] :return: bool """ if self.verifier.check_dt_perm(dt, cdt): return True if not self.verifier.check_dt_owner(dt, owner_address): return False data, cdt_ddo = resolve_asset(cdt, self.dt_factory) if not data or not cdt_ddo: return False consume_address = data[1] original_msg = f'{consume_address}{cdt}' if not self.verifier.verify_signature(consume_address, signature, original_msg): return False checksum = data[2] if not self.verifier.verify_ddo_integrity(cdt_ddo, checksum): return False if not self.verifier.verify_services(cdt_ddo, [dt], False): return False return True def get_dt_marketplace(self): """ Get all available dts in the marketplace. :return: list """ dt_idx, _, issuers, checksums, _, ipfs_paths, _ = self.dt_factory.get_available_dts( ) issuer_names = self.asset_provider.get_issuer_names(issuers) marketplace_list = [] for dt, issuer_name, ipfs_path, checksum in zip( dt_idx, issuer_names, ipfs_paths, checksums): ddo = resolve_asset_by_url(ipfs_path) if ddo and ddo.metadata['main'].get('type') != "Algorithm": if self.verifier.verify_ddo_integrity(ddo, checksum): dt = DTHelper.id_bytes_to_dt(dt) asset_name = ddo.metadata["main"].get("name") asset_fig = ddo.metadata['main'].get('fig') union_or_not = ddo.is_cdt marketplace_list.append({ "dt": dt, "issuer": issuer_name, "name": asset_name, "fig": asset_fig, "union_or_not": union_or_not }) return marketplace_list def get_dt_details(self, dt): """ Get the detailed information given a datatoken. :param dt: refers to dt identifier :return: tuple """ data, ddo = resolve_asset(dt, self.dt_factory) if not data or not ddo: return None checksum = data[2] if not self.verifier.verify_ddo_integrity(ddo, checksum): return None owner = data[0] issuer = data[1] issuer_name = self.asset_provider.get_enterprise(issuer)[0] asset_name = ddo.metadata['main'].get('name') asset_desc = ddo.metadata['main'].get('desc') asset_type = ddo.metadata['main'].get('type') asset_fig = ddo.metadata['main'].get('fig') dt_info = { "name": asset_name, "owner": owner, "issuer": issuer_name, "desc": asset_desc, "type": asset_type, "fig": asset_fig } union_data = None if ddo.is_cdt: union_paths = self.tracer.trace_data_union(ddo, [ddo.dt]) tree = self.tracer.tree_format(union_paths) union_data = self.tracer.tree_to_json(tree) # self.tracer.print_tree(tree, indent=[], final_node=True) service_lists = [] for service in ddo.services: sid = service.index op_name = service.attributes.get('op_name') price = service.attributes['price'] constrains = service.descriptor service_lists.append({ "sid": sid, "op": op_name, "price": price, "constrains": constrains }) return (dt_info, service_lists, union_data)
class JobService(): """The entry point for accessing the job service.""" def __init__(self, config): keeper = Keeper(config.keeper_options) self.dt_factory = keeper.dt_factory self.op_template = keeper.op_template self.task_market = keeper.task_market self.verifier = VerifierService(config) self.config = config def create_task(self, name, desc, demander_wallet): """ Add a new task on chain. :param name: refers to the task name :param desc: refers to the task description :param demander_wallet: demander account :return: int task_id """ task_id = self.task_market.create_task(name, desc, demander_wallet) return task_id def add_job(self, task_id, cdt, solver_wallet): """ Create a new job on chain with the algorithm cdt. :param cdt: refers to the algorithm composable data token :param task_id: refers to the task id that be solved :param from_wallet: solver account :return: int job_id """ _id = DTHelper.dt_to_id(cdt) job_id = self.task_market.add_job(_id, task_id, solver_wallet) return job_id def check_remote_compute(self, cdt, dt, job_id, owner_address, signature): """ Check job status and resource permissions automatically when receiving an on-premise computation request, used by Compute-to-Data. :param cdt: refers to cdt identifier provided by solver :param dt: refers to dt identifier owned by the provider grid :param job_id: refers to job identifier in the task market :param owner_address: asset owner address :param signature: signed by solver, [solver_address, job_id] :return: bool """ if not self.verifier.verify_job_registered(job_id, cdt): return False if not self.verifier.check_dt_owner(dt, owner_address): return False data, cdt_ddo = resolve_asset(cdt, self.dt_factory) if not data or not cdt_ddo: return False if not self.verifier.check_asset_type(cdt_ddo, 'Algorithm'): return False solver_address = data[1] checksum = data[2] original_msg = f'{solver_address}{job_id}' if not self.verifier.verify_signature(solver_address, signature, original_msg): return False if not self.verifier.verify_ddo_integrity(cdt_ddo, checksum): return False if not self.verifier.verify_perms_ready(cdt_ddo, required_dt=dt): return False return True def fetch_exec_code(self, cdt, leaf_dt): """ Get the code template and its fulfiled arguments, given a father cdt and a leaf dt. The father ddo specifies which child service/template to use. :param cdt: father composable data token :param leaf_dt: child data token, must be leaf :return: bool """ _, cdt_ddo = resolve_asset(cdt, self.dt_factory) _, dt_ddo = resolve_asset(leaf_dt, self.dt_factory) fulfilled = cdt_ddo.services[0].descriptor['workflow'].get(leaf_dt) if not fulfilled: return None, None sid = fulfilled.get('service') args = fulfilled.get('constraint') tid = dt_ddo.get_service_by_index(sid).descriptor['template'] _, op = resolve_op(tid, self.op_template) return op.operation, args