def has_immutable_data(user_zonefile, data_hash):
    """
    Does the given user have the given immutable data?
    Return True if so
    Return False if not
    """
    assert is_user_zonefile(user_zonefile)

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash), 'Invalid data hash "{}"'.format(data_hash)

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

        except AssertionError as ae:
            log.error("Invalid immutable data hash")
            continue

        if data_hash == h:
            return True

    return False
Exemple #2
0
def remove_immutable_data_zonefile(user_zonefile, data_hash):
    """
    Remove a data hash from a user's zonefile.
    Return True if removed
    Return False if not present
    """
    assert is_user_zonefile(user_zonefile)

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash), 'Invalid data hash "{}"'.format(data_hash)

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

        except AssertionError as ae:
            log.debug("Invalid immutable data hash")
            continue

        if data_hash == h:
            user_zonefile['txt'].remove(txtrec)
            return True

    return False
def remove_immutable_data_zonefile(user_zonefile, data_hash):
    """
    Remove a data hash from a user's zonefile.
    Return True if removed
    Return False if not present
    """
    assert is_user_zonefile(user_zonefile)

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash), 'Invalid data hash "{}"'.format(data_hash)

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

        except AssertionError as ae:
            log.debug("Invalid immutable data hash")
            continue

        if data_hash == h:
            user_zonefile['txt'].remove(txtrec)
            return True

    return False
Exemple #4
0
def has_immutable_data(user_zonefile, data_hash):
    """
    Does the given user have the given immutable data?
    Return True if so
    Return False if not
    """
    assert is_user_zonefile(user_zonefile)

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash), 'Invalid data hash "{}"'.format(data_hash)

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

        except AssertionError as ae:
            log.error("Invalid immutable data hash")
            continue

        if data_hash == h:
            return True

    return False
Exemple #5
0
def put_immutable_data_zonefile(user_zonefile, data_id, data_hash, data_url=None):
    """
    Add a data hash to a user's zonefile.  Make sure it's a valid hash as well.
    Return True on success
    Return False otherwise.
    """

    if not is_user_zonefile(user_zonefile):
        log.debug("Invalid zone file structure")
        return False

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash)

    k = get_immutable_data_hashes(user_zonefile, data_id)
    if k is not None and len(k) > 0:
        # exists or name collision
        log.debug("collision on {} ({})".format(data_id, k))
        return k[0] == data_hash

    txtrec = '#{}'.format(data_hash)
    if data_url is not None:
        txtrec = '{}{}'.format(data_url, txtrec)

    user_zonefile.setdefault('txt', [])

    name_txt = {'name': data_id, 'txt': txtrec}
    user_zonefile['txt'].append(name_txt)

    return True
Exemple #6
0
def has_immutable_data_id(user_zonefile, data_id):
    """
    Does the given user have the given immutable data?
    Return True if so
    Return False if not
    """
    if not is_user_zonefile(user_zonefile):
        log.debug("Not a valid zone file")
        return False

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        d_id = None
        try:
            d_id = txtrec['name']
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)
        except AssertionError:
            continue

        if data_id == d_id:
            return True

    return False
def get_immutable_data_url(user_zonefile, data_hash):
    """
    Given the hash of an immutable datum, find the associated
    URL hint (if given)
    Return None if not given, or not found.
    """

    assert is_user_zonefile(user_zonefile)

    if 'txt' not in user_zonefile:
        return None

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

            if data_hash != h:
                continue

            url = get_immutable_url_from_txt(txtrec['txt'])
        except AssertionError as ae:
            log.debug("Invalid immutable data hash {}".format(h))
            continue

        return url

    return None
Exemple #8
0
def get_immutable_data_url(user_zonefile, data_hash):
    """
    Given the hash of an immutable datum, find the associated
    URL hint (if given)
    Return None if not given, or not found.
    """

    assert is_user_zonefile(user_zonefile)

    if 'txt' not in user_zonefile:
        return None

    for txtrec in user_zonefile['txt']:
        h = None
        try:
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)

            if data_hash != h:
                continue

            url = get_immutable_url_from_txt(txtrec['txt'])
        except AssertionError as ae:
            log.debug("Invalid immutable data hash {}".format(h))
            continue

        return url

    return None
def has_immutable_data_id(user_zonefile, data_id):
    """
    Does the given user have the given immutable data?
    Return True if so
    Return False if not
    """
    if not is_user_zonefile(user_zonefile):
        log.debug("Not a valid zone file")
        return False

    if 'txt' not in user_zonefile:
        return False

    for txtrec in user_zonefile['txt']:
        d_id = None
        try:
            d_id = txtrec['name']
            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            assert scripts.is_valid_hash(h)
        except AssertionError:
            continue

        if data_id == d_id:
            return True

    return False
def put_immutable_data_zonefile(user_zonefile, data_id, data_hash, data_url=None):
    """
    Add a data hash to a user's zonefile.  Make sure it's a valid hash as well.
    Return True on success
    Return False otherwise.
    """

    if not is_user_zonefile(user_zonefile):
        log.debug("Invalid zone file structure")
        return False

    data_hash = str(data_hash)
    assert scripts.is_valid_hash(data_hash)

    k = get_immutable_data_hashes(user_zonefile, data_id)
    if k is not None and len(k) > 0:
        # exists or name collision
        log.debug("collision on {} ({})".format(data_id, k))
        return k[0] == data_hash

    txtrec = '#{}'.format(data_hash)
    if data_url is not None:
        txtrec = '{}{}'.format(data_url, txtrec)

    user_zonefile.setdefault('txt', [])

    name_txt = {'name': data_id, 'txt': txtrec}
    user_zonefile['txt'].append(name_txt)

    return True
Exemple #11
0
def get_immutable_hash_from_txt(txtrec):
    """
    Given an immutable data txt record,
    get the hash.
    The hash is the suffix that begins with #.
    Return None if invalid or not present
    """
    if '#' not in txtrec:
        return None

    h = txtrec.split('#')[-1]
    if not scripts.is_valid_hash(h):
        return None

    return h
def get_immutable_hash_from_txt(txtrec):
    """
    Given an immutable data txt record,
    get the hash.
    The hash is the suffix that begins with #.
    Return None if invalid or not present
    """
    if '#' not in txtrec:
        return None

    h = txtrec.split('#')[-1]
    if not scripts.is_valid_hash(h):
        return None

    return h
Exemple #13
0
def blockstack_immutable_data_url(blockchain_id, data_id, data_hash):
    """
    Make a blockstack:// URL for immutable data
    data_id must be url-quoted
    """
    assert re.match(schemas.OP_URLENCODED_PATTERN, data_id)

    if data_hash is not None and not is_valid_hash(data_hash):
        raise ValueError('Invalid hash: {}'.format(data_hash))

    if data_hash is not None:
        return 'blockstack://{}.{}/#{}'.format(data_id,
                                               urllib.quote(blockchain_id),
                                               data_hash)

    return 'blockstack://{}.{}'.format(data_id, urllib.quote(blockchain_id))
Exemple #14
0
def blockstack_immutable_data_url_parse(url):
    """
    Parse a blockstack:// URL for immutable data
    Return (blockchain ID, data ID, data hash)
    * The hash may be None if not given, in which case, the hash should be looked up from the blockchain ID's profile.
    * The data ID may be None, in which case, the list of immutable data is requested.

    Raise on bad data
    """

    url = str(url)
    immutable_data_regex = r'^blockstack://({}+)\.({}+)\.({}+)[/]*([/]+#[a-fA-F0-9]+)?$'.format(
        URLENCODED_CLASS, B40_NO_PERIOD_CLASS, B40_NO_PERIOD_CLASS)
    immutable_listing_regex = r'^blockstack://({}+)[/]+#immutable$'.format(
        B40_CLASS)

    m = re.match(immutable_data_regex, url)
    if m:
        data_id, blockchain_name, namespace_id, data_hash = m.groups()
        blockchain_id = '{}.{}'.format(blockchain_name, namespace_id)

        if not is_name_valid(blockchain_id):
            log.debug('Invalid blockstack ID "{}"'.format(blockchain_id))
            raise ValueError('Invalid blockstack ID')

        if data_hash is not None:
            data_hash = data_hash.lower().strip('#/')
            if not is_valid_hash(data_hash):
                log.debug('Invalid data hash "{}"'.format(data_hash))
                raise ValueError('Invalid data hash')

        return urllib.unquote(blockchain_id), data_id, data_hash
    else:
        # maybe a listing?
        m = re.match(immutable_listing_regex, url)
        if not m:
            log.debug('Invalid immutable URL "{}"'.format(url))
            raise ValueError('Invalid immutable URL')

        blockchain_id = m.groups()[0]
        return urllib.unquote(blockchain_id), None, None

    return None, None, None
Exemple #15
0
def list_immutable_data(user_zonefile):
    """
    Get the IDs and hashes of all immutable data
    Return [(data ID, hash)]
    """
    assert is_user_zonefile(user_zonefile)

    ret = []
    if 'txt' not in user_zonefile:
        return ret

    for txtrec in user_zonefile['txt']:
        try:
            d_id = txtrec['name']
            h = get_immutable_hash_from_txt(txtrec['txt'])
            assert scripts.is_valid_hash(h)
            ret.append((d_id, h))
        except AssertionError as ae:
            log.error("Invalid immutable data hash")
            continue

    return ret
def list_immutable_data(user_zonefile):
    """
    Get the IDs and hashes of all immutable data
    Return [(data ID, hash)]
    """
    assert is_user_zonefile(user_zonefile)

    ret = []
    if 'txt' not in user_zonefile:
        return ret

    for txtrec in user_zonefile['txt']:
        try:
            d_id = txtrec['name']
            h = get_immutable_hash_from_txt(txtrec['txt'])
            assert scripts.is_valid_hash(h)
            ret.append((d_id, h))
        except AssertionError as ae:
            log.error("Invalid immutable data hash")
            continue

    return ret
def get_immutable_data_hashes(user_zonefile, data_id):
    """
    Get the hash of an immutable datum by name.
    Return None if there is no match.
    Return the list of hashes otherwise
    """
    assert is_user_zonefile(user_zonefile)

    if 'txt' not in user_zonefile:
        return None

    ret = None
    for txtrec in user_zonefile['txt']:
        h, d_id = None, None

        try:
            d_id = txtrec['name']
            if data_id != d_id:
                continue

            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            msg = 'Invalid data hash for "{}" (got "{}" from {})'
            assert scripts.is_valid_hash(h), msg.format(d_id, h, txtrec['txt'])
        except AssertionError as ae:
            if BLOCKSTACK_TEST is not None:
                log.exception(ae)

            continue

        if ret is None:
            ret = [h]
        else:
            ret.append(h)

    return ret
Exemple #18
0
def get_immutable_data_hashes(user_zonefile, data_id):
    """
    Get the hash of an immutable datum by name.
    Return None if there is no match.
    Return the list of hashes otherwise
    """
    assert is_user_zonefile(user_zonefile)

    if 'txt' not in user_zonefile:
        return None

    ret = None
    for txtrec in user_zonefile['txt']:
        h, d_id = None, None

        try:
            d_id = txtrec['name']
            if data_id != d_id:
                continue

            h = get_immutable_hash_from_txt(txtrec['txt'])
            if h is None:
                continue

            msg = 'Invalid data hash for "{}" (got "{}" from {})'
            assert scripts.is_valid_hash(h), msg.format(d_id, h, txtrec['txt'])
        except AssertionError as ae:
            if BLOCKSTACK_TEST is not None:
                log.exception(ae)

            continue

        if ret is None:
            ret = [h]
        else:
            ret.append(h)

    return ret