def put_immutable(name, data, privatekey, txid=None, proxy=None):
    """
    put_immutable

    Optionally include a txid from the user record update, in order to retry a failed
    data replication (in which case, this txid corresponds to the succeeded name
    update operation).  This is to avoid needing to pay for each replication retry.
    """

    if proxy is None:
        global default_proxy
        proxy = default_proxy

    # need to put the transaction ID into the data record we put
    user = get_name_record(name, create_if_absent=True)
    if 'error' in user:

        # no user data
        return {'error': "Unable to load user record: %s" % user['error']}

    data_hash = storage.get_data_hash(data)
    rc = user_db.add_immutable_data(user, data_hash)
    if not rc:
        return {'error': 'Invalid hash'}

    user_json = user_db.serialize_user(user)
    if user_json is None:
        raise Exception("BUG: failed to serialize user record")

    value_hash = None

    if txid is None:

        # haven't updated the user record yet.  Do so now.
        # put the new user record hash
        update_result = update(name, user_json, privatekey, proxy=proxy)
        if 'error' in update_result:

            # failed to replicate user record
            # NOTE: result will have the txid in it; pass it as txid to try again!
            return update_result

        txid = update_result['transaction_hash']
        value_hash = update_result['value_hash']

    result = {'data_hash': data_hash, 'transaction_hash': txid}

    # propagate update() data
    if value_hash is not None:
        result['value_hash'] = value_hash

    # replicate the data
    rc = storage.put_immutable_data(data, txid)
    if not rc:
        result['error'] = 'Failed to store immutable data'
        return result

    else:
        result['status'] = True
        return result
Exemple #2
0
def store_name_zonefile_data(name, user_zonefile_txt, txid, storage_drivers=None):
    """
    Store a serialized zonefile to immutable storage providers, synchronously.
    This is only necessary if we've added/changed/removed immutable data.

    Return (True, hash(user zonefile)) on success
    Return (False, None) on failure.
    """

    storage_drivers = [] if storage_drivers is None else storage_drivers

    data_hash = storage.get_zonefile_data_hash(user_zonefile_txt)

    result = storage.put_immutable_data(
        user_zonefile_txt, txid, data_hash=data_hash,
        required=storage_drivers
    )

    rc = bool(result)

    return rc, data_hash
def store_name_zonefile_data(name, user_zonefile_txt, txid, storage_drivers=None):
    """
    Store a serialized zonefile to immutable storage providers, synchronously.
    This is only necessary if we've added/changed/removed immutable data.

    Return (True, hash(user zonefile)) on success
    Return (False, None) on failure.
    """

    storage_drivers = [] if storage_drivers is None else storage_drivers

    data_hash = storage.get_zonefile_data_hash(user_zonefile_txt)

    result = storage.put_immutable_data(
        user_zonefile_txt, txid, data_hash=data_hash,
        required=storage_drivers
    )

    rc = bool(result)

    return rc, data_hash
Exemple #4
0
def put_immutable(name, data_id, data_json, data_url=None, txid=None, proxy=None, utxo_client=None, wallet_keys=None ):
    """
    put_immutable

    Given a user's name, the data ID, and a JSON-ified chunk of data,
    put it into the user's zonefile.

    If the user's zonefile corresponds to a legacy profile, then automatically
    convert it into a mutable profile and a modern zonefile, and then proceed
    to add the data record.

    If @txid is given, then don't re-send the NAME_UPDATE.  Just try to store
    the data to the immutable storage providers (again).  This is to allow
    for retries in the case where the NAME_UPDATE went through but the
    storage providers did not receive data.
    
    Return {'status': True, 'transaction_hash': txid, 'immutable_data_hash': data_hash, ...} on success
    Return {'error': ...} on error
    """

    from backend.nameops import do_update

    if type(data_json) not in [dict]:
        raise ValueError("Immutable data must be a dict")

    legacy = False
    if proxy is None:
        proxy = get_default_proxy()

    user_profile, user_zonefile, legacy = get_and_migrate_profile( name, create_if_absent=True, proxy=proxy, wallet_keys=wallet_keys )
    if 'error' in user_profile:
        log.debug("Unable to load user zonefile for '%s'" % name)
        return user_profile
   
    if legacy:
        log.debug("User profile is legacy")
        return {'error': "User profile is in legacy format, which does not support this operation.  You must first migrate it with the 'migrate' command."}

    data_text = storage.serialize_immutable_data( data_json )
    data_hash = storage.get_data_hash( data_text )

    # insert into user zonefile, overwriting if need be
    if user_db.has_immutable_data_id( user_zonefile, data_id ):
        log.debug("WARN: overwriting old '%s'" % data_id)
        old_hash = user_db.get_immutable_data_hash( user_zonefile, data_id )

        # NOTE: can be a list, if the name matches multiple hashes.
        # this tool doesn't do this, but it's still possible for the user to use other tools to do this.
        if type(old_hash) != list:
            old_hash = [old_hash]

        for oh in old_hash:
            rc = user_db.remove_immutable_data_zonefile( user_zonefile, oh )
            if not rc:
                return {'error': 'Failed to overwrite old immutable data'}

    rc = user_db.put_immutable_data_zonefile( user_zonefile, data_id, data_hash, data_url=data_url )
    if not rc:
        return {'error': 'Failed to insert immutable data into user zonefile'}

    zonefile_hash = hash_zonefile( user_zonefile )

    # update zonefile, if we haven't already
    if txid is None:
        _, payment_privkey = get_payment_keypair(wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        _, owner_privkey = get_owner_keypair(wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        utxo_client = get_utxo_provider_client( config_path=proxy.conf['path'] )
        broadcaster_client = get_tx_broadcaster( config_path=proxy.conf['path'] )

        update_result = do_update( name, zonefile_hash, owner_privkey, payment_privkey, utxo_client, broadcaster_client, config_path=proxy.conf['path'], proxy=proxy )
        if 'error' in update_result:
            # failed to replicate user zonefile hash 
            # the caller should simply try again, with the 'transaction_hash' given in the result.
            return update_result

        txid = update_result['transaction_hash']

    result = {
        'immutable_data_hash': data_hash,
        'transaction_hash': txid,
        'zonefile_hash': zonefile_hash
    }

    # replicate immutable data 
    rc = storage.put_immutable_data( data_json, txid )
    if not rc:
        result['error'] = 'Failed to store immutable data'
        return result

    rc = store_name_zonefile( name, user_zonefile, txid )
    if not rc:
        result['error'] = 'Failed to store zonefile'
        return result

    # success!
    result['status'] = True
    return result
def put_immutable(name, data, privatekey, txid=None, proxy=None):
    """
    put_immutable

    Optionally include a txid from the user record update, in order to retry a failed
    data replication (in which case, this txid corresponds to the succeeded name
    update operation).  This is to avoid needing to pay for each replication retry.
    """

    if proxy is None:
        global default_proxy
        proxy = default_proxy

    # need to put the transaction ID into the data record we put
    user = get_name_record(name, create_if_absent=True)
    if 'error' in user:

        # no user data
        return {'error': "Unable to load user record: %s" % user['error']}

    data_hash = storage.get_data_hash( data )
    rc = user_db.add_immutable_data(user, data_hash)
    if not rc:
        return {'error': 'Invalid hash'}

    user_json = user_db.serialize_user(user)
    if user_json is None:
        raise Exception("BUG: failed to serialize user record")

    value_hash = None

    if txid is None:

        # haven't updated the user record yet.  Do so now.
        # put the new user record hash
        update_result = update(name, user_json, privatekey, proxy=proxy)
        if 'error' in update_result:

            # failed to replicate user record
            # NOTE: result will have the txid in it; pass it as txid to try again!
            return update_result

        txid = update_result['transaction_hash']
        value_hash = update_result['value_hash']

    result = {
        'data_hash': data_hash,
        'transaction_hash': txid
    }

    # propagate update() data
    if value_hash is not None:
        result['value_hash'] = value_hash

    # replicate the data
    rc = storage.put_immutable_data(data, txid)
    if not rc:
        result['error'] = 'Failed to store immutable data'
        return result

    else:
        result['status'] = True
        return result
    Return (True, hash(user)) on success
    Return (False, hash(user)) on failure
    """

    username = user_db.name(user)

    # serialize
    user_json = None
    try:
        user_json = user_db.serialize_user(user)
    except Exception, e:
        log.error("Failed to serialize '%s'" % user)
        return False

    data_hash = storage.get_data_hash(user_json)
    result = storage.put_immutable_data(user_json, txid )

    rc = None
    if result is None:
        rc = False
    else:
        rc = True

    return (rc, data_hash)


def remove_name_record(user, txid):
    """
    Delete JSON user record data from immutable storage providers, synchronously.

    Return (True, hash(user)) on success
    Return (True, hash(user)) on success
    Return (False, hash(user)) on failure
    """

    username = user_db.name(user)

    # serialize
    user_json = None
    try:
        user_json = user_db.serialize_user(user)
    except Exception, e:
        log.error("Failed to serialize '%s'" % user)
        return False

    data_hash = storage.get_data_hash(user_json)
    result = storage.put_immutable_data(user_json, txid)

    rc = None
    if result is None:
        rc = False
    else:
        rc = True

    return (rc, data_hash)


def remove_name_record(user, txid):
    """
    Delete JSON user record data from immutable storage providers, synchronously.

    Return (True, hash(user)) on success