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
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))
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)))