def load_user(record_hash): """ Load a user record from the storage implementation with the given hex string hash, The user record hash should have been loaded from the blockchain, and thereby be the authentic hash. Return the user record on success Return None on error """ user_json = storage.get_immutable_data(record_hash) if user_json is None: log.error("Failed to load user record '%s'" % record_hash) return None # verify integrity user_record_hash = storage.get_data_hash(user_json) if user_record_hash != record_hash: log.error( "Profile hash mismatch: expected '%s', got '%s'" % record_hash, user_record_hash) return None user = user_db.parse_user(user_json) return user
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
def remove_name_zonefile(user, txid): """ Delete JSON user zonefile data from immutable storage providers, synchronously. Return (True, hash(user)) on success Return (False, hash(user)) on error """ # serialize user_json = json.dumps(user, sort_keys=True) data_hash = storage.get_data_hash(user_json) result = storage.delete_immutable_data(data_hash, txid) rc = bool(result) return rc, data_hash
def load_user(record_hash): """ Load a user record from the storage implementation with the given hex string hash, The user record hash should have been loaded from the blockchain, and thereby be the authentic hash. Return the user record on success Return None on error """ user_json = storage.get_immutable_data(record_hash) if user_json is None: log.error("Failed to load user record '%s'" % record_hash) return None # verify integrity user_record_hash = storage.get_data_hash(user_json) if user_record_hash != record_hash: log.error("Profile hash mismatch: expected '%s', got '%s'" % record_hash, user_record_hash) return None user = user_db.parse_user(user_json) return user
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 (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.