def get_mutable(name, data_id, ver_min=None, ver_max=None, ver_check=None, conf=None): """ get_mutable """ if conf is None: conf = config.get_config() user = get_name_record(name) if 'error' in user: # no user data return {'error': "Unable to load user record: %s" % user['error']} # find the mutable data ID data_route = user_db.get_mutable_data_route(user, data_id) if data_route is None: # no data return {'error': 'No such route'} # go and fetch the data data = storage.get_mutable_data(data_route, ver_min=ver_min, ver_max=ver_max, ver_check=ver_check) if data is None: # no data return {'error': 'No mutable data found'} # what's the expected version of this data? expected_version = load_mutable_data_version( conf, name, data_id, try_remote=False ) if expected_version is not None: if expected_version > data['ver']: return {'error': 'Stale data', 'ver_minimum': expected_version, 'ver_received': data['ver']} elif ver_check is None: # we don't have a local version, and the caller didn't check it. log.warning("Unconfirmed version for data '%s'" % data_id) data['warning'] = "Unconfirmed version" # remember latest version if data['ver'] > expected_version: store_mutable_data_version( conf, data_id, data['ver'] ) # include the route data['route'] = data_route return data
def delete_mutable(name, data_id, privatekey, proxy=default_proxy, txid=None, route=None): """ delete_mutable """ if proxy is None: proxy = get_default_proxy() result = {} user = get_name_record(name) if 'error' in user: # no user data return {'error': "Unable to load user record: %s" % user['error']} # does the user have a route to this data? if not user_db.has_mutable_data_route(user, data_id) and txid is None: # nope--we're good return {'status': True} # blow away the data storage_rc = storage.delete_mutable_data(data_id, privatekey) if not storage_rc: result['error'] = "Failed to delete mutable data" return result # remove the route from the user record user_db.remove_mutable_data_route(user, data_id) user_json = user_db.serialize_user(user) # update the user record update_status = update(name, user_json, privatekey, txid=txid, proxy=proxy) if 'error' in update_status: # failed; caller should try again return update_status if txid is None: txid = update_status['transaction_hash'] # blow away the route if route is None: route = user_db.get_mutable_data_route(user, data_id) route_hash = storage.get_mutable_data_route_hash(route) storage_rc = storage.delete_immutable_data(route_hash, txid) if not storage_rc: result['error'] = "Failed to delete immutable data route" result['route'] = route else: result['status'] = True result['transaction_hash'] = txid result['value_hash'] = update_status['value_hash'] # uncache local version delete_mutable_data_version( config.get_config(), data_id ) return result
def put_mutable(name, data_id, data_text, privatekey, proxy=None, create=True, txid=None, ver=None, make_ver=None, conf=None ): """ put_mutable ** Consistency ** ver, if given, is the version to include in the data. make_ver, if given, is a callback that takes the data_id, data_text, and current version as arguments, and generates the version to be included in the data record uploaded. If ver is not given, but make_ver is, then make_ver will be used to generate the version. If neither ver nor make_ver are given, the mutable data (if it already exists) is fetched, and the version is calculated as the larget known version + 1. ** Durability ** Replication is best-effort. If one storage provider driver succeeds, the put_mutable succeeds. If they all fail, then put_mutable fails. More complex behavior can be had by creating a "meta-driver" that calls existing drivers' methods in the desired manner. """ if proxy is None: proxy = get_default_proxy() result = {} user = get_name_record(name, create_if_absent=create) if 'error' in user: return {'error': "Unable to load user record: %s" % user['error']} route = None exists = user_db.has_mutable_data_route(user, data_id) old_hash = None cur_hash = None new_ver = ver if ver is None: if exists: # mutable record already exists. # generate one automatically. # use the existing locally-stored version, # and fall back to using the last-known version # from the existing mutable data record. new_ver = load_mutable_data_version( config.get_config(), name, data_id, try_remote=True ) if new_ver is None: # data exists, but we couldn't figure out the version return {'error': "Unable to determine version"} if make_ver is not None: # generate version new_ver = make_ver( data_id, data_text, new_ver ) else: # no version known, and no way to generate it. # by default, start at 1. we'll manage it ourselves. if new_ver is None: new_ver = 1 else: new_ver += 1 # do we have a route for this data yet? if not exists: if not create: # won't create; expect it to exist return {'error': 'No such route'} # need to put one urls = storage.make_mutable_urls(data_id) if len(urls) == 0: return {"error": "No routes constructed"} writer_pubkey = pybitcointools.privkey_to_pubkey(privatekey) route = storage.mutable_data_route(data_id, urls, writer_pubkey=writer_pubkey) user_db.add_mutable_data_route(user, route) user_json = user_db.serialize_user(user) # update the user record with the new route update_result = update(name, user_json, privatekey, txid=txid, proxy=proxy) if 'error' in update_result: # update failed; caller should try again return update_result txid = update_result['transaction_hash'] cur_hash = update_result['value_hash'] else: route = user_db.get_mutable_data_route(user, data_id) if route is None: return {"error": "No such route"} # generate the data data = storage.mutable_data(data_id, data_text, new_ver, privkey=privatekey) if data is None: return {"error": "Failed to generate data record"} # serialize... data_json = parsing.json_stable_serialize(data) # replicate... store_rc = storage.put_mutable_data( data, privatekey ) if not store_rc: result['error'] = "Failed to store mutable data" else: result['status'] = True result['transaction_hash'] = txid if cur_hash: # propagate result['value_hash'] = cur_hash # cache new version store_mutable_data_version( conf, data_id, new_ver ) return result
def delete_mutable(name, data_id, privatekey, proxy=default_proxy, txid=None, route=None): """ delete_mutable """ if proxy is None: proxy = get_default_proxy() result = {} user = get_name_record(name) if 'error' in user: # no user data return {'error': "Unable to load user record: %s" % user['error']} # does the user have a route to this data? if not user_db.has_mutable_data_route(user, data_id) and txid is None: # nope--we're good return {'status': True} # blow away the data storage_rc = storage.delete_mutable_data(data_id, privatekey) if not storage_rc: result['error'] = "Failed to delete mutable data" return result # remove the route from the user record user_db.remove_mutable_data_route(user, data_id) user_json = user_db.serialize_user(user) # update the user record update_status = update(name, user_json, privatekey, txid=txid, proxy=proxy) if 'error' in update_status: # failed; caller should try again return update_status if txid is None: txid = update_status['transaction_hash'] # blow away the route if route is None: route = user_db.get_mutable_data_route(user, data_id) route_hash = storage.get_mutable_data_route_hash(route) storage_rc = storage.delete_immutable_data(route_hash, txid) if not storage_rc: result['error'] = "Failed to delete immutable data route" result['route'] = route else: result['status'] = True result['transaction_hash'] = txid result['value_hash'] = update_status['value_hash'] # uncache local version delete_mutable_data_version(config.get_config(), data_id) return result
def put_mutable(name, data_id, data_text, privatekey, proxy=None, create=True, txid=None, ver=None, make_ver=None, conf=None): """ put_mutable ** Consistency ** ver, if given, is the version to include in the data. make_ver, if given, is a callback that takes the data_id, data_text, and current version as arguments, and generates the version to be included in the data record uploaded. If ver is not given, but make_ver is, then make_ver will be used to generate the version. If neither ver nor make_ver are given, the mutable data (if it already exists) is fetched, and the version is calculated as the larget known version + 1. ** Durability ** Replication is best-effort. If one storage provider driver succeeds, the put_mutable succeeds. If they all fail, then put_mutable fails. More complex behavior can be had by creating a "meta-driver" that calls existing drivers' methods in the desired manner. """ if proxy is None: proxy = get_default_proxy() result = {} user = get_name_record(name, create_if_absent=create) if 'error' in user: return {'error': "Unable to load user record: %s" % user['error']} route = None exists = user_db.has_mutable_data_route(user, data_id) old_hash = None cur_hash = None new_ver = ver if ver is None: if exists: # mutable record already exists. # generate one automatically. # use the existing locally-stored version, # and fall back to using the last-known version # from the existing mutable data record. new_ver = load_mutable_data_version(config.get_config(), name, data_id, try_remote=True) if new_ver is None: # data exists, but we couldn't figure out the version return {'error': "Unable to determine version"} if make_ver is not None: # generate version new_ver = make_ver(data_id, data_text, new_ver) else: # no version known, and no way to generate it. # by default, start at 1. we'll manage it ourselves. if new_ver is None: new_ver = 1 else: new_ver += 1 # do we have a route for this data yet? if not exists: if not create: # won't create; expect it to exist return {'error': 'No such route'} # need to put one urls = storage.make_mutable_urls(data_id) if len(urls) == 0: return {"error": "No routes constructed"} writer_pubkey = pybitcointools.privkey_to_pubkey(privatekey) route = storage.mutable_data_route(data_id, urls, writer_pubkey=writer_pubkey) user_db.add_mutable_data_route(user, route) user_json = user_db.serialize_user(user) # update the user record with the new route update_result = update(name, user_json, privatekey, txid=txid, proxy=proxy) if 'error' in update_result: # update failed; caller should try again return update_result txid = update_result['transaction_hash'] cur_hash = update_result['value_hash'] else: route = user_db.get_mutable_data_route(user, data_id) if route is None: return {"error": "No such route"} # generate the data data = storage.mutable_data(data_id, data_text, new_ver, privkey=privatekey) if data is None: return {"error": "Failed to generate data record"} # serialize... data_json = parsing.json_stable_serialize(data) # replicate... store_rc = storage.put_mutable_data(data, privatekey) if not store_rc: result['error'] = "Failed to store mutable data" else: result['status'] = True result['transaction_hash'] = txid if cur_hash: # propagate result['value_hash'] = cur_hash # cache new version store_mutable_data_version(conf, data_id, new_ver) return result
def get_mutable(name, data_id, ver_min=None, ver_max=None, ver_check=None, conf=None): """ get_mutable """ if conf is None: conf = config.get_config() user = get_name_record(name) if 'error' in user: # no user data return {'error': "Unable to load user record: %s" % user['error']} # find the mutable data ID data_route = user_db.get_mutable_data_route(user, data_id) if data_route is None: # no data return {'error': 'No such route'} # go and fetch the data data = storage.get_mutable_data(data_route, ver_min=ver_min, ver_max=ver_max, ver_check=ver_check) if data is None: # no data return {'error': 'No mutable data found'} # what's the expected version of this data? expected_version = load_mutable_data_version(conf, name, data_id, try_remote=False) if expected_version is not None: if expected_version > data['ver']: return { 'error': 'Stale data', 'ver_minimum': expected_version, 'ver_received': data['ver'] } elif ver_check is None: # we don't have a local version, and the caller didn't check it. log.warning("Unconfirmed version for data '%s'" % data_id) data['warning'] = "Unconfirmed version" # remember latest version if data['ver'] > expected_version: store_mutable_data_version(conf, data_id, data['ver']) # include the route data['route'] = data_route return data