def close_connection(handle, h2c_spend_secret=None): cursor = sql.get_cursor() hub_connection = db.hub_connection(handle=handle, cursor=cursor) # save secret if given and not already known if h2c_spend_secret is not None: secret_hash = util.hash160hex(h2c_spend_secret) if not get_secret(secret_hash): db.add_secret(secret_value=h2c_spend_secret, secret_hash=secret_hash, cursor=cursor) # close connection if not already done if not hub_connection["closed"]: db.set_connection_closed(handle=handle, cursor=cursor) # get c2h spend secret if no commits for channel c2h_spend_secret = None c2h_state = db.load_channel_state(hub_connection["c2h_channel_id"], hub_connection["asset"], cursor=cursor) if len(c2h_state["commits_active"]) == 0: c2h_spend_secret_hash = scripts.get_deposit_spend_secret_hash( c2h_state["deposit_script"]) c2h_spend_secret = get_secret(c2h_spend_secret_hash) hub_key = db.channel_payer_key(id=hub_connection["h2c_channel_id"]) return ({"spend_secret": c2h_spend_secret}, hub_key["wif"])
def publish_commits(): with etc.database_lock: commit_rawtxs = [] cursor = sql.get_cursor() for hub_connection in db.hub_connections_complete(cursor=cursor): asset = hub_connection["asset"] c2h_mpc_id = hub_connection["c2h_channel_id"] c2h_state = db.load_channel_state(c2h_mpc_id, asset, cursor=cursor) h2c_mpc_id = hub_connection["h2c_channel_id"] h2c_state = db.load_channel_state(h2c_mpc_id, asset, cursor=cursor) h2c_spend_secret_hash = get_deposit_spend_secret_hash( h2c_state["deposit_script"] ) h2c_spend_secret = lib.get_secret(h2c_spend_secret_hash) c2h_expired = lib.is_expired(c2h_state, etc.expire_clearance) h2c_expired = lib.is_expired(h2c_state, etc.expire_clearance) expired = c2h_expired or h2c_expired h2c_commits_published = api.mpc_published_commits(state=h2c_state) closed = hub_connection["closed"] != 0 # connection expired or commit published or spend secret known if expired or closed or h2c_commits_published or h2c_spend_secret: if not closed: db.set_connection_closed(handle=hub_connection["handle"]) rawtx = Mpc(api).finalize_commit(lib.get_wif, c2h_state) if rawtx: commit_rawtxs.append(rawtx) return commit_rawtxs
def close_connection(handle, h2c_spend_secret=None): cursor = sql.get_cursor() hub_connection = db.hub_connection(handle=handle, cursor=cursor) # save secret if given and not already known if h2c_spend_secret is not None: secret_hash = util.hash160hex(h2c_spend_secret) if not get_secret(secret_hash): db.add_secret(secret_value=h2c_spend_secret, secret_hash=secret_hash, cursor=cursor) # close connection if not already done if not hub_connection["closed"]: db.set_connection_closed(handle=handle, cursor=cursor) # get c2h spend secret if no commits for channel c2h_spend_secret = None c2h_state = db.load_channel_state(hub_connection["c2h_channel_id"], hub_connection["asset"], cursor=cursor) if len(c2h_state["commits_active"]) == 0: c2h_spend_secret_hash = scripts.get_deposit_spend_secret_hash( c2h_state["deposit_script"] ) c2h_spend_secret = get_secret(c2h_spend_secret_hash) hub_wif = load_wif() return ({"spend_secret": c2h_spend_secret}, hub_wif)
def sync_hub_connection(handle, next_revoke_secret_hash, payments, commit, revokes): cursor = sql.get_cursor() hub_connection = db.hub_connection(handle=handle, cursor=cursor) _update_channel_state(hub_connection, commit, revokes, cursor) _process_payments(handle, payments, hub_connection, cursor) _balance_channel(handle, cursor) next_revoke_secret = create_secret() # create next spend secret # load unnotified c2h_id = hub_connection["c2h_channel_id"] h2c_id = hub_connection["h2c_channel_id"] h2c_commit = db.unnotified_commit(channel_id=h2c_id) c2h_revokes = db.unnotified_revokes(channel_id=c2h_id) receive_payments = db.unnotified_payments(payee_handle=handle) # save sync data h2c_commit_id = None if h2c_commit: h2c_commit_id = h2c_commit.pop("id") _save_sync_data(cursor, handle, next_revoke_secret_hash, receive_payments, h2c_commit_id, c2h_revokes, c2h_id, next_revoke_secret) hub_key = db.channel_payer_key(id=h2c_id) return ({ "receive": receive_payments, "commit": h2c_commit, "revokes": [r["revoke_secret"] for r in c2h_revokes], "next_revoke_secret_hash": next_revoke_secret["secret_hash"] }, hub_key["wif"])
def publish_commits(): with etc.database_lock: txids = [] cursor = sql.get_cursor() for hub_connection in db.hub_connections_complete(cursor=cursor): asset = hub_connection["asset"] c2h_mpc_id = hub_connection["c2h_channel_id"] c2h_state = db.load_channel_state(c2h_mpc_id, asset, cursor=cursor) h2c_mpc_id = hub_connection["h2c_channel_id"] h2c_state = db.load_channel_state(h2c_mpc_id, asset, cursor=cursor) h2c_spend_secret_hash = get_deposit_spend_secret_hash( h2c_state["deposit_script"]) h2c_spend_secret = lib.get_secret(h2c_spend_secret_hash) c2h_expired = lib.is_expired(c2h_state, etc.expire_clearance) h2c_expired = lib.is_expired(h2c_state, etc.expire_clearance) expired = c2h_expired or h2c_expired h2c_commits_published = api.mpc_published_commits(state=h2c_state) closed = hub_connection["closed"] != 0 # connection expired or commit published or spend secret known if expired or closed or h2c_commits_published or h2c_spend_secret: if not closed: db.set_connection_closed(handle=hub_connection["handle"]) c2h_commits_published = api.mpc_published_commits( state=c2h_state) if len(c2h_commits_published) == 0: txid = Mpc(api).finalize_commit(lib.get_wif, c2h_state) if txid: txids.append(txid) return txids
def fund_deposits(): """Fund or top off open channels.""" with etc.database_lock: deposits = [] cursor = sql.get_cursor() for hub_connection in db.hub_connections_open(cursor=cursor): asset = hub_connection["asset"] terms = db.terms(id=hub_connection["terms_id"], cursor=cursor) # load client to hub data c2h_mpc_id = hub_connection["c2h_channel_id"] c2h_state = db.load_channel_state(c2h_mpc_id, asset, cursor=cursor) c2h_deposit_address = lib.deposit_address(c2h_state) c2h_deposit_balance = lib.get_balances(c2h_deposit_address, assets=[asset])[asset] if c2h_deposit_balance < terms["deposit_min"]: continue # ignore if client deposit insufficient if lib.is_expired(c2h_state, etc.expire_clearance): continue # ignore if expires soon if lib.has_unconfirmed_transactions(c2h_deposit_address): continue # ignore if unconfirmed transaction inputs/outputs if api.mpc_published_commits(state=c2h_state): continue # ignore if c2h commit published # load hub to client data h2c_mpc_id = hub_connection["h2c_channel_id"] h2c_state = db.load_channel_state(h2c_mpc_id, asset, cursor=cursor) h2c_deposit_address = lib.deposit_address(h2c_state) h2c_deposit_balance = lib.get_balances(h2c_deposit_address, assets=[asset])[asset] if lib.is_expired(h2c_state, etc.expire_clearance): continue # ignore if expires soon if lib.has_unconfirmed_transactions(h2c_deposit_address): continue # ignore if unconfirmed transaction inputs/outputs if api.mpc_published_commits(state=h2c_state): continue # ignore if h2c commit published # fund hub to client if needed deposit_max = terms["deposit_max"] deposit_ratio = terms["deposit_ratio"] if deposit_max: target = min(deposit_max, c2h_deposit_balance) * deposit_ratio else: target = int(c2h_deposit_balance * deposit_ratio) quantity = target - h2c_deposit_balance if quantity > 0: txid = lib.send_funds(h2c_deposit_address, asset, quantity) deposits.append({ "txid": txid, "asset": asset, "address": h2c_deposit_address, "quantity": quantity, "handle": hub_connection["handle"] }) return deposits
def recover_funds(): """Recover funds where possible""" with etc.database_lock: txs = [] cursor = sql.get_cursor() for hub_connection in db.hub_connections_recoverable(cursor=cursor): txs += lib.recover_funds(hub_connection, cursor=cursor) return txs
def recover_funds(): """Recover funds where possible""" with etc.database_lock: rawtxs = { "payout": {}, "revoke": {}, "change": {}, "expire": {}, "commit": {} } cursor = sql.get_cursor() for hub_connection in db.hub_connections_recoverable(cursor=cursor): result = lib.recover_funds(hub_connection, cursor=cursor) rawtxs = _merge_rawtxs(rawtxs, result) return rawtxs
def complete_hub_connection(data, cursor=None): cursor = cursor or sql.get_cursor() cursor.execute("BEGIN TRANSACTION") set_next_revoke_secret_hash( handle=data["handle"], next_revoke_secret_hash=data["next_revoke_secret_hash"], cursor=cursor) sql.execute(_COMPLETE_CONNECTION, data, cursor=cursor) add_revoke_secret_args = { "secret_hash": data["secret_hash"], "secret_value": data["secret_value"], "channel_id": data["c2h_channel_id"], } sql.execute(_ADD_REVOKE_SECRET, args=add_revoke_secret_args, cursor=cursor) cursor.execute("COMMIT")
def complete_hub_connection(data, cursor=None): cursor = cursor or sql.get_cursor() cursor.execute("BEGIN TRANSACTION") set_next_revoke_secret_hash( handle=data["handle"], next_revoke_secret_hash=data["next_revoke_secret_hash"], cursor=cursor ) sql.execute(_COMPLETE_CONNECTION, data, cursor=cursor) add_revoke_secret_args = { "secret_hash": data["secret_hash"], "secret_value": data["secret_value"], "channel_id": data["c2h_channel_id"], } sql.execute(_ADD_REVOKE_SECRET, args=add_revoke_secret_args, cursor=cursor) cursor.execute("COMMIT")
def save_channel_state(channel_id, state, h2c_unnotified_commit=None, unnotified_revoke_secrets=None, cursor=None): cursor = cursor or sql.get_cursor() # reformat state data commits_requested = _fmt_requested(channel_id, state["commits_requested"]) commits_active = _fmt_active(channel_id, h2c_unnotified_commit, state["commits_active"]) commits_revoked = _fmt_revoked( channel_id, state["commits_revoked"], h2c_unnotified_commit=h2c_unnotified_commit, unnotified_revoke_secrets=unnotified_revoke_secrets ) # save state to db cursor.execute(_RM_COMMITS, {"channel_id": channel_id}) cursor.executemany(_ADD_COMMIT_REQUESTED, commits_requested) cursor.executemany(_ADD_COMMIT_ACTIVE, commits_active) cursor.executemany(_ADD_COMMIT_REVOKED, commits_revoked)
def sync_hub_connection(handle, next_revoke_secret_hash, payments, commit, revokes): cursor = sql.get_cursor() hub_connection = db.hub_connection(handle=handle, cursor=cursor) _update_channel_state(hub_connection, commit, revokes, cursor) _process_payments(handle, payments, hub_connection, cursor) _balance_channel(handle, cursor) next_revoke_secret = create_secret() # create next spend secret # load unnotified c2h_id = hub_connection["c2h_channel_id"] h2c_id = hub_connection["h2c_channel_id"] h2c_commit = db.unnotified_commit(channel_id=h2c_id) c2h_revokes = db.unnotified_revokes(channel_id=c2h_id) receive_payments = db.unnotified_payments(payee_handle=handle) # save sync data h2c_commit_id = None if h2c_commit: h2c_commit_id = h2c_commit.pop("id") _save_sync_data( cursor, handle, next_revoke_secret_hash, receive_payments, h2c_commit_id, c2h_revokes, c2h_id, next_revoke_secret ) hub_wif = load_wif() return ( { "receive": receive_payments, "commit": h2c_commit, "revokes": [r["revoke_secret"] for r in c2h_revokes], "next_revoke_secret_hash": next_revoke_secret["secret_hash"] }, hub_wif )
def add_hub_connection(data, cursor=None): cursor = cursor or sql.get_cursor() cursor.execute("BEGIN TRANSACTION") sql.execute(_ADD_HUB_CONNECTION, data, cursor=cursor) cursor.execute("COMMIT")
def add_keys(keys, cursor=None): cursor = cursor or sql.get_cursor() cursor.execute("BEGIN TRANSACTION") cursor.executemany(_ADD_KEY, keys) cursor.execute("COMMIT")
def set_revokes_notified(revoke_ids, cursor=None): cursor = cursor or sql.get_cursor() cursor.executemany(_SET_REVOKE_NOTIFIED, revoke_ids)
def set_payments_notified(payment_ids, cursor=None): cursor = cursor or sql.get_cursor() cursor.executemany(_SET_PAYMENT_NOTIFIED, payment_ids)
def handles_exist(handles, cursor=None): args = [(handle,) for handle in handles] cursor = cursor or sql.get_cursor() result = cursor.executemany(_HANDLE_EXISTS, args).fetchall() return all([r[0] for r in result])