Example #1
0
def delete_immutable_data(data_hash, txid, privkey=None, signed_data_tombstone=None):
    """
    Given the hash of the data, the private key of the user,
    and the txid that deleted the data's hash from the blockchain,
    delete the data from all immutable data stores.
    """

    global storage_handlers

    # sanity check
    if not is_singlesig_hex(privkey):
        log.error('Only single-signature data private keys are supported')
        return False

    if signed_data_tombstone is None:
        assert privkey
        data_hash = str(data_hash)
        txid = str(txid)

        ts = make_data_tombstone('immutable:{}:{}'.format(data_hash, txid))
        signed_data_tombstone = sign_data_tombstone( ts, privkey )
        
    for handler in storage_handlers:
        if not getattr(handler, 'delete_immutable_handler', None):
            continue

        try:
            handler.delete_immutable_handler(data_hash, txid, signed_data_tombstone)
        except Exception as e:
            log.exception(e)
            return False

    return True
Example #2
0
def delete_mutable_data(fq_data_id,
                        privatekey=None,
                        signed_data_tombstone=None,
                        required=None,
                        required_exclusive=False,
                        skip=None,
                        blockchain_id=None,
                        profile=False):
    """
    Given the data ID and private key of a user,
    go and delete the associated mutable data.

    The fq_data_id is an opaque identifier that is prefixed with the username.
    """

    global storage_handlers

    assert privatekey or signed_data_tombstone

    required = [] if required is None else required
    skip = [] if skip is None else skip

    # fully-qualified username hint
    fqu = None
    if blockchain_id is not None:
        fqu = blockchain_id

    # sanity check
    if privatekey is not None and not is_singlesig_hex(privatekey):
        log.error('Only single-signature data private keys are supported')
        return False

    fq_data_id = str(fq_data_id)
    if signed_data_tombstone is None:
        assert privatekey
        ts = make_data_tombstone(fq_data_id)
        signed_data_tombstone = sign_data_tombstone(ts, privatekey)

    required_successes = 0

    # remove data
    for handler in storage_handlers:
        if handler.__name__ in skip:
            log.debug("Skipping {}".format(handler.__name__))
            continue

        if not getattr(handler, 'delete_mutable_handler', None):
            continue

        if required_exclusive and handler.__name__ not in required:
            log.debug("Skipping non-required driver {}".format(
                handler.__name__))
            continue

        rc = False
        try:
            rc = handler.delete_mutable_handler(fq_data_id,
                                                signed_data_tombstone,
                                                fqu=fqu,
                                                profile=profile)
        except Exception as e:
            log.exception(e)
            rc = False

        if not rc and handler.__name__ in required:
            log.error(
                "Failed to delete from required storage driver {}".format(
                    handler.__name__))
            return False

        elif handler.__name__ in required:
            required_successes += 1

    return required_successes >= len(set(required) - set(skip))
Example #3
0
def put_mutable_data(fq_data_id,
                     data_text_or_json,
                     sign=True,
                     raw=False,
                     data_privkey=None,
                     data_pubkey=None,
                     data_signature=None,
                     profile=False,
                     blockchain_id=None,
                     required=None,
                     skip=None,
                     required_exclusive=False):
    """
    Given the unserialized data, store it into our mutable data stores.
    Do so in a best-effort way.  This method fails if all storage providers fail,
    or if a storage provider in required fails.

    @required: list of required drivers to use.  All of them must succeed for this method to succeed.
    @skip: list of drivers we can skip.  None of them will be tried.
    @required_exclusive: if True, then only the required drivers will be tried (none of the loaded but not-required drivers will be invoked)
    @sign: if True, then a private key is required.  if False, then simply store the data without serializing it or including a public key and signature.
    @raw: If True, then the data will be put as-is without any ancilliary metadata.  Requires sign=False

    Return True on success
    Return False on error
    """

    global storage_handlers
    assert len(storage_handlers) > 0, "No storage handlers initialized"

    # sanity check: only take structured data if this is a profile
    if not isinstance(data_text_or_json, (str, unicode)):
        assert profile, "Structured data is only supported when profile=True"

    required = [] if required is None else required
    skip = [] if skip is None else skip

    assert len(set(required).intersection(
        set(skip))) == 0, "Overlap between required and skip driver lists"

    log.debug(
        'put_mutable_data({}), required={}, skip={} required_exclusive={}'.
        format(fq_data_id, ','.join(required), ','.join(skip),
               required_exclusive))

    # fully-qualified username hint
    fqu = None
    if blockchain_id is not None:
        fqu = blockchain_id

    # sanity check: only support single-sig private keys
    if data_privkey is not None:
        if not is_singlesig_hex(data_privkey):
            log.error('Only single-signature data private keys are supported')
            return False

        data_pubkey = get_pubkey_hex(data_privkey)

    elif sign:
        assert data_pubkey is not None
        assert data_signature is not None

    serialized_data = None
    if sign or not raw:
        serialized_data = serialize_mutable_data(data_text_or_json,
                                                 data_privkey=data_privkey,
                                                 data_pubkey=data_pubkey,
                                                 data_signature=data_signature,
                                                 profile=profile)
    else:
        serialized_data = data_text_or_json

    if BLOCKSTACK_TEST:
        log.debug("data ({}): {}".format(type(serialized_data),
                                         serialized_data))

    successes = 0
    required_successes = 0

    for handler in storage_handlers:
        if handler.__name__ in skip:
            log.debug("Skipping {}: at caller's request".format(
                handler.__name__))
            continue

        if not getattr(handler, 'put_mutable_handler', None):
            if handler.__name__ not in required:
                log.debug(
                    "Skipping {}: it does not implement put_mutable_handler".
                    format(handler.__name__))
                continue

            log.debug(
                "Required storage provider {} does not implement put_mutable_handler"
                .format(handler.__name__))
            return False

        if required_exclusive and handler.__name__ not in required:
            log.debug("Skipping {}: it is optional".format(handler.__name__))
            continue

        rc = False
        log.debug('Try "{}"'.format(handler.__name__))

        try:
            rc = handler.put_mutable_handler(fq_data_id,
                                             serialized_data,
                                             fqu=fqu,
                                             profile=profile)
        except Exception as e:
            log.exception(e)
            if handler.__name__ not in required:
                continue

            log.error("Failed to replicate data with '{}'".format(
                handler.__name__))
            return None

        if rc:
            log.debug("Replicated {} bytes with {} (rc = {})".format(
                len(serialized_data), handler.__name__, rc))
            successes += 1

            if handler.__name__ in required:
                required_successes += 1

            continue

        if handler.__name__ not in required:
            log.debug('Failed to replicate with "{}"'.format(handler.__name__))
            continue

        # required driver failed
        log.error(
            "Failed to replicate to required storage provider '{}'".format(
                handler.__name__))
        return False

    # failed everywhere or succeeded somewhere
    log.debug(
        "put_mutable_data: successes = {}, required_successes = {}, |required - skip| = {}"
        .format(successes, required_successes, len(set(required) - set(skip))))

    return (successes > 0) and (required_successes >=
                                len(set(required) - set(skip)))