def app_put_resource( blockchain_id, app_domain, res_name, res_data, app_config=None, data_privkey=None, proxy=None, wallet_keys=None, config_path=CONFIG_PATH ): """ Store data to a named application resource in mutable storage. data_privkey should be the publisher's private key name should be a blockchain ID that points to the public key if app_config is not None, then the driver hints will be honored. Return {'status': True, 'version': ...} on success Return {'error': ...} on error """ assert isinstance(res_data, (str, unicode)), "Resource must be a string" try: json.dumps(res_data) except: raise AssertionError("Resource must be a JSON-serializable string") if data_privkey is None: assert wallet_keys, 'Missing both data private key and wallet keys' data_privkey = wallet_keys.get('data_privkey') assert data_privkey, "Wallet does not have a data private key" proxy = get_default_proxy() if proxy is None else proxy res_data_id = storage.make_fq_data_id(app_domain, res_name) data_pubkey = get_pubkey_hex(data_privkey) driver_hints = None if app_config is not None: # use driver hints driver_hints = app_config['driver_hints'] res_blob = data.make_mutable_data_info(res_data_id, res_data, is_fq_data_id=True) res_blob_str = data.data_blob_serialize(res_blob) res_sig = data.data_blob_sign(res_blob_str, data_privkey) res = data.put_mutable(res_data_id, res_blob_str, data_pubkey, res_sig, res_blob['version'], blockchain_id=blockchain_id, config_path=config_path, storage_drivers=driver_hints) if 'error' in res: log.error("Failed to store resource {}: {}".format(res_data_id, res['error'])) return {'error': 'Failed to store resource'} return {'status': True, 'version': res_blob['version']}
def app_put_resource(blockchain_id, app_domain, res_name, res_data, app_config=None, data_privkey=None, proxy=None, wallet_keys=None, config_path=CONFIG_PATH): """ Store data to a named application resource in mutable storage. data_privkey should be the publisher's private key name should be a blockchain ID that points to the public key if app_config is not None, then the driver hints will be honored. Return {'status': True, 'version': ...} on success Return {'error': ...} on error """ assert isinstance(res_data, (str, unicode)), "Resource must be a string" try: json.dumps(res_data) except: raise AssertionError("Resource must be a JSON-serializable string") if data_privkey is None: assert wallet_keys, 'Missing both data private key and wallet keys' data_privkey = wallet_keys.get('data_privkey') assert data_privkey, "Wallet does not have a data private key" proxy = get_default_proxy() if proxy is None else proxy res_data_id = storage.make_fq_data_id(app_domain, res_name) data_pubkey = get_pubkey_hex(data_privkey) driver_hints = None if app_config is not None: # use driver hints driver_hints = app_config['driver_hints'] res_blob = data.make_mutable_data_info(res_data_id, res_data, is_fq_data_id=True) res_blob_str = data.data_blob_serialize(res_blob) res_sig = data.data_blob_sign(res_blob_str, data_privkey) res = data.put_mutable(res_data_id, res_blob_str, data_pubkey, res_sig, res_blob['version'], blockchain_id=blockchain_id, config_path=config_path, storage_drivers=driver_hints) if 'error' in res: log.error("Failed to store resource {}: {}".format( res_data_id, res['error'])) return {'error': 'Failed to store resource'} return {'status': True, 'version': res_blob['version']}
def app_publish( dev_blockchain_id, app_domain, app_method_list, app_index_uris, app_index_file, app_driver_hints=[], data_privkey=None, proxy=None, wallet_keys=None, config_path=CONFIG_PATH ): """ Instantiate an application. * replicate the (opaque) app index file to "index.html" to each URL in app_uris * replicate the list of URIs and the list of methods to ".blockstack" via each of the client's storage drivers. This succeeds even if the app already exists (in which case, it will be overwritten). This method is idempotent, so it can be retried on failure. data_privkey should be the publisher's private key (i.e. their data key) name should be the blockchain ID that points to data_pubkey Return {'status': True, 'config_fq_data_id': config's fully-qualified data ID, 'index_fq_data_id': index file's fully-qualified data ID} on success Return {'error': ...} on error """ if data_privkey is None: assert wallet_keys, 'Missing both data private key and wallet keys' data_privkey = wallet_keys.get('data_privkey') assert data_privkey, "Wallet does not have a data private key" proxy = get_default_proxy() if proxy is None else proxy # replicate configuration data (method list and app URIs) app_cfg = { 'blockchain_id': dev_blockchain_id, 'app_domain': app_domain, 'index_uris': app_index_uris, 'api_methods': app_method_list, 'driver_hints': app_driver_hints, } jsonschema.validate(app_cfg, APP_CONFIG_SCHEMA) data_pubkey = get_pubkey_hex(data_privkey) config_data_id = storage.make_fq_data_id(app_domain, '.blockstack') app_cfg_blob = data.make_mutable_data_info(config_data_id, app_cfg, is_fq_data_id=True) app_cfg_str = data.data_blob_serialize(app_cfg_blob) app_cfg_sig = data.data_blob_sign( app_cfg_str, data_privkey ) res = data.put_mutable(config_data_id, app_cfg_str, data_pubkey, app_cfg_sig, app_cfg_blob['version'], blockchain_id=dev_blockchain_id, config_path=config_path) if 'error' in res: log.error('Failed to replicate application configuration {}: {}'.format(config_data_id, res['error'])) return {'error': 'Failed to replicate application config'} # what drivers to use for the index file? urls = user_db.urls_from_uris(app_index_uris) driver_names = [] for url in urls: drivers = storage.get_drivers_for_url(url) driver_names += [d.__name__ for d in drivers] driver_names = list(set(driver_names)) index_data_id = storage.make_fq_data_id(app_domain, 'index.html') # replicate app index file (at least one must succeed) # NOTE: the publisher is free to use alternative URIs that are not supported; they'll just be ignored. app_index_blob = data.make_mutable_data_info(index_data_id, app_index_file, is_fq_data_id=True) app_index_blob_str = data.data_blob_serialize(app_index_blob) app_index_sig = data.data_blob_sign(app_index_blob_str, data_privkey) res = data.put_mutable( index_data_id, app_index_blob_str, data_pubkey, app_index_sig, app_index_blob['version'], blockchain_id=dev_blockchain_id, config_path=config_path, storage_drivers=app_driver_hints ) if 'error' in res: log.error("Failed to replicate application index file to {}: {}".format(",".join(urls), res['error'])) return {'error': 'Failed to replicate index file'} return {'status': True, 'config_fq_data_id': config_data_id, 'index_fq_data_id': index_data_id}
def app_publish(dev_blockchain_id, app_domain, app_method_list, app_index_uris, app_index_file, app_driver_hints=[], data_privkey=None, proxy=None, wallet_keys=None, config_path=CONFIG_PATH): """ Instantiate an application. * replicate the (opaque) app index file to "index.html" to each URL in app_uris * replicate the list of URIs and the list of methods to ".blockstack" via each of the client's storage drivers. This succeeds even if the app already exists (in which case, it will be overwritten). This method is idempotent, so it can be retried on failure. data_privkey should be the publisher's private key (i.e. their data key) name should be the blockchain ID that points to data_pubkey Return {'status': True, 'config_fq_data_id': config's fully-qualified data ID, 'index_fq_data_id': index file's fully-qualified data ID} on success Return {'error': ...} on error """ if data_privkey is None: assert wallet_keys, 'Missing both data private key and wallet keys' data_privkey = wallet_keys.get('data_privkey') assert data_privkey, "Wallet does not have a data private key" proxy = get_default_proxy() if proxy is None else proxy # replicate configuration data (method list and app URIs) app_cfg = { 'blockchain_id': dev_blockchain_id, 'app_domain': app_domain, 'index_uris': app_index_uris, 'api_methods': app_method_list, 'driver_hints': app_driver_hints, } jsonschema.validate(app_cfg, APP_CONFIG_SCHEMA) data_pubkey = get_pubkey_hex(data_privkey) config_data_id = storage.make_fq_data_id(app_domain, '.blockstack') app_cfg_blob = data.make_mutable_data_info(config_data_id, app_cfg, is_fq_data_id=True) app_cfg_str = data.data_blob_serialize(app_cfg_blob) app_cfg_sig = data.data_blob_sign(app_cfg_str, data_privkey) res = data.put_mutable(config_data_id, app_cfg_str, data_pubkey, app_cfg_sig, app_cfg_blob['version'], blockchain_id=dev_blockchain_id, config_path=config_path) if 'error' in res: log.error( 'Failed to replicate application configuration {}: {}'.format( config_data_id, res['error'])) return {'error': 'Failed to replicate application config'} # what drivers to use for the index file? urls = user_db.urls_from_uris(app_index_uris) driver_names = [] for url in urls: drivers = storage.get_drivers_for_url(url) driver_names += [d.__name__ for d in drivers] driver_names = list(set(driver_names)) index_data_id = storage.make_fq_data_id(app_domain, 'index.html') # replicate app index file (at least one must succeed) # NOTE: the publisher is free to use alternative URIs that are not supported; they'll just be ignored. app_index_blob = data.make_mutable_data_info(index_data_id, app_index_file, is_fq_data_id=True) app_index_blob_str = data.data_blob_serialize(app_index_blob) app_index_sig = data.data_blob_sign(app_index_blob_str, data_privkey) res = data.put_mutable(index_data_id, app_index_blob_str, data_pubkey, app_index_sig, app_index_blob['version'], blockchain_id=dev_blockchain_id, config_path=config_path, storage_drivers=app_driver_hints) if 'error' in res: log.error( "Failed to replicate application index file to {}: {}".format( ",".join(urls), res['error'])) return {'error': 'Failed to replicate index file'} return { 'status': True, 'config_fq_data_id': config_data_id, 'index_fq_data_id': index_data_id }