Ejemplo n.º 1
0
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
    """

    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)

    config_data_id = '{}/.blockstack'.format(app_domain)
    res = data.put_mutable(config_data_id, app_cfg, blockchain_id=dev_blockchain_id, data_privkey=data_privkey, wallet_keys=wallet_keys, config_path=config_path, fully_qualified_data_id=True)
    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 = "{}/index.html".format(app_domain)
    
    # 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.
    res = data.put_mutable( index_data_id, app_index_file, blockchain_id=dev_blockchain_id, data_privkey=data_privkey, storage_drivers=driver_names, wallet_keys=wallet_keys, config_path=config_path, fully_qualified_data_id=True)
    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}
Ejemplo n.º 2
0
def app_unpublish( blockchain_id, app_domain, force=False, data_privkey=None, app_config=None, wallet_keys=None, proxy=None, config_path=CONFIG_PATH ):
    """
    Unpublish an application
    Deletes its config and index.
    Does NOT delete its resources.
    Does NOT delete user data.

    if force is True, then we will try to delete the app state even if we can't load the app config
    WARNING: force can be dangerous, since it can delete data via drivers that were never meant for this app.  Use with caution!

    Return {'status': True, 'app_config': ..., 'retry': ...} on success.  If retry is True, then retry this method with the given app_config
    Return {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    # find out where to delete from
    data_pubkey = None
    if data_privkey is not None:
        data_pubkey = get_pubkey_hex(str(data_privkey))

    if app_config is None:
        app_config = app_get_config(blockchain_id, app_domain, data_pubkey=data_pubkey, proxy=proxy, config_path=CONFIG_PATH )
        if 'error' in app_config:
            if not force:
                log.error("Failed to load app config for {}'s {}".format(blockchain_id, app_domain))
                return {'error': 'Failed to load app config'}
            else:
                # keep going 
                app_config = None
                log.warning("Failed to load app config, but proceeding at caller request")

    config_data_id = storage.make_fq_data_id(app_domain, '.blockstack')
    index_data_id = storage.make_fq_data_id(app_domain, 'index.html')

    storage_drivers = None
    if app_config is not None:
        # only use the ones we have to 
        urls = user_db.urls_from_uris(app_config['index_uris'])
        driver_names = []

        for url in urls:
            drivers = storage.get_drivers_for_url(url)
            driver_names += [d.__name__ for d in drivers]

        storage_drivers = list(set(driver_names))
    
    ret = {}

    # delete the index
    index_tombstone = storage.make_data_tombstone(index_data_id)
    signed_index_tombstone = storage.sign_data_tombstone(index_data_id, data_privkey)
    res = data.delete_mutable(index_data_id, [signed_index_tombstone], proxy=proxy, storage_drivers=storage_drivers, blockchain_id=blockchain_id, is_fq_data_id=True, config_path=config_path)
    if 'error' in res:
        log.warning("Failed to delete index file {}".format(index_data_id))
        ret['app_config'] = app_config
        ret['retry'] = True

    # delete the config 
    config_tombstone = storage.make_data_tombstone(config_data_id)
    signed_config_tombstone = storage.sign_data_tombstone(config_data_id, data_privkey)
    res = data.delete_mutable(config_data_id, [signed_config_tombstone], proxy=proxy, blockchain_id=blockchain_id, is_fq_data_id=True, config_path=config_path)
    if 'error' in res:
        log.warning("Failed to delete config file {}".format(config_data_id))
        if not ret.has_key('app_config'):
            ret['app_config'] = app_config

        ret['retry'] = True

    ret['status'] = True
    return ret
Ejemplo n.º 3
0
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}
Ejemplo n.º 4
0
def app_unpublish(blockchain_id,
                  app_domain,
                  force=False,
                  data_privkey=None,
                  app_config=None,
                  wallet_keys=None,
                  proxy=None,
                  config_path=CONFIG_PATH):
    """
    Unpublish an application
    Deletes its config and index.
    Does NOT delete its resources.
    Does NOT delete user data.

    if force is True, then we will try to delete the app state even if we can't load the app config
    WARNING: force can be dangerous, since it can delete data via drivers that were never meant for this app.  Use with caution!

    Return {'status': True, 'app_config': ..., 'retry': ...} on success.  If retry is True, then retry this method with the given app_config
    Return {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    # find out where to delete from
    data_pubkey = None
    if data_privkey is not None:
        data_pubkey = get_pubkey_hex(str(data_privkey))

    if app_config is None:
        app_config = app_get_config(blockchain_id,
                                    app_domain,
                                    data_pubkey=data_pubkey,
                                    proxy=proxy,
                                    config_path=CONFIG_PATH)
        if 'error' in app_config:
            if not force:
                log.error("Failed to load app config for {}'s {}".format(
                    blockchain_id, app_domain))
                return {'error': 'Failed to load app config'}
            else:
                # keep going
                app_config = None
                log.warning(
                    "Failed to load app config, but proceeding at caller request"
                )

    config_data_id = storage.make_fq_data_id(app_domain, '.blockstack')
    index_data_id = storage.make_fq_data_id(app_domain, 'index.html')

    storage_drivers = None
    if app_config is not None:
        # only use the ones we have to
        urls = user_db.urls_from_uris(app_config['index_uris'])
        driver_names = []

        for url in urls:
            drivers = storage.get_drivers_for_url(url)
            driver_names += [d.__name__ for d in drivers]

        storage_drivers = list(set(driver_names))

    ret = {}

    # delete the index
    index_tombstone = storage.make_data_tombstone(index_data_id)
    signed_index_tombstone = storage.sign_data_tombstone(
        index_data_id, data_privkey)
    res = data.delete_mutable(index_data_id, [signed_index_tombstone],
                              proxy=proxy,
                              storage_drivers=storage_drivers,
                              blockchain_id=blockchain_id,
                              is_fq_data_id=True,
                              config_path=config_path)
    if 'error' in res:
        log.warning("Failed to delete index file {}".format(index_data_id))
        ret['app_config'] = app_config
        ret['retry'] = True

    # delete the config
    config_tombstone = storage.make_data_tombstone(config_data_id)
    signed_config_tombstone = storage.sign_data_tombstone(
        config_data_id, data_privkey)
    res = data.delete_mutable(config_data_id, [signed_config_tombstone],
                              proxy=proxy,
                              blockchain_id=blockchain_id,
                              is_fq_data_id=True,
                              config_path=config_path)
    if 'error' in res:
        log.warning("Failed to delete config file {}".format(config_data_id))
        if not ret.has_key('app_config'):
            ret['app_config'] = app_config

        ret['retry'] = True

    ret['status'] = True
    return ret