def propagate_exchange(cursor, participant, exchange, route, error, amount): """Propagates an exchange's result to the participant's balance and the route's status. """ route.update_error(error or '') new_balance = cursor.one( """ UPDATE participants SET balance=(balance + %s) WHERE id=%s RETURNING balance """, (amount, participant.id)) if amount < 0 and new_balance < 0: raise NegativeBalance if amount < 0: bundles = cursor.all( """ LOCK TABLE cash_bundles IN EXCLUSIVE MODE; SELECT * FROM cash_bundles WHERE owner = %s AND ts < now() - INTERVAL %s ORDER BY ts """, (participant.id, QUARANTINE)) withdrawable = sum(b.amount for b in bundles) x = -amount if x > withdrawable: raise NotEnoughWithdrawableMoney(Money(withdrawable, 'EUR')) for b in bundles: if x >= b.amount: cursor.run("DELETE FROM cash_bundles WHERE id = %s", (b.id, )) x -= b.amount if x == 0: break else: assert x > 0 cursor.run( """ UPDATE cash_bundles SET amount = (amount - %s) WHERE id = %s """, (x, b.id)) break elif amount > 0: cursor.run( """ INSERT INTO cash_bundles (owner, origin, amount, ts) VALUES (%s, %s, %s, %s) """, (participant.id, exchange.id, amount, exchange.timestamp)) participant.set_attributes(balance=new_balance) if amount != 0: participant.update_giving_and_tippees(cursor)
def propagate_exchange(cursor, participant, exchange, error, amount, bundles=None): """Propagates an exchange's result to the participant's balance. """ wallet_id = exchange.wallet_id new_balance = cursor.one(""" UPDATE wallets SET balance = (balance + %s) WHERE remote_id = %s AND (balance + %s) >= 0 RETURNING balance """, (amount, wallet_id, amount)) if new_balance is None: raise NegativeBalance if amount < 0: bundles = bundles or cursor.all(""" LOCK TABLE cash_bundles IN EXCLUSIVE MODE; SELECT b.* FROM cash_bundles b JOIN exchanges e ON e.id = b.origin WHERE b.owner = %s AND b.ts < now() - INTERVAL %s AND b.disputed IS NOT TRUE AND b.locked_for IS NULL AND b.amount::currency = %s ORDER BY b.owner = e.participant DESC, b.ts """, (participant.id, QUARANTINE, amount.currency)) withdrawable = sum(b.amount for b in bundles) x = -amount if x > withdrawable: raise NotEnoughWithdrawableMoney(withdrawable) for b in bundles: if x >= b.amount: cursor.run(""" UPDATE cash_bundles SET owner = NULL , withdrawal = %s , wallet_id = NULL WHERE id = %s """, (exchange.id, b.id)) x -= b.amount if x == 0: break else: assert x > 0 cursor.run(""" INSERT INTO cash_bundles (owner, origin, ts, amount, withdrawal, wallet_id) VALUES (NULL, %s, %s, %s, %s, NULL) """, (b.origin, b.ts, x, exchange.id)) cursor.run(""" UPDATE cash_bundles SET amount = (amount - %s) WHERE id = %s """, (x, b.id)) break elif amount > 0 and (exchange.amount < 0 or exchange.refund_ref): # failed withdrawal orig_exchange_id = exchange.refund_ref or exchange.id cursor.run(""" UPDATE cash_bundles b SET owner = %(p_id)s , withdrawal = NULL , wallet_id = %(wallet_id)s WHERE withdrawal = %(e_id)s """, dict(p_id=participant.id, e_id=orig_exchange_id, wallet_id=wallet_id)) elif amount > 0: cursor.run(""" INSERT INTO cash_bundles (owner, origin, amount, ts, wallet_id) VALUES (%s, %s, %s, %s, %s) """, (participant.id, exchange.id, amount, exchange.timestamp, wallet_id)) new_balance = cursor.one("SELECT recompute_balance(%s)", (participant.id,)) participant.set_attributes(balance=new_balance) if amount != 0: merge_cash_bundles(cursor, participant.id) participant.update_giving_and_tippees(cursor)
def propagate_exchange(cursor, participant, exchange, route, error, amount): """Propagates an exchange's result to the participant's balance and the route's status. """ route.update_error(error or '') new_balance = cursor.one( """ UPDATE participants SET balance=(balance + %s) WHERE id=%s RETURNING balance """, (amount, participant.id)) if amount < 0 and new_balance < 0: raise NegativeBalance if amount < 0: bundles = cursor.all( """ LOCK TABLE cash_bundles IN EXCLUSIVE MODE; SELECT b.* FROM cash_bundles b JOIN exchanges e ON e.id = b.origin WHERE b.owner = %s AND b.ts < now() - INTERVAL %s ORDER BY b.owner = e.participant DESC, b.ts """, (participant.id, QUARANTINE)) withdrawable = sum(b.amount for b in bundles) x = -amount if x > withdrawable: raise NotEnoughWithdrawableMoney(Money(withdrawable, 'EUR')) for b in bundles: if x >= b.amount: cursor.run( """ INSERT INTO e2e_transfers (origin, withdrawal, amount) VALUES (%s, %s, %s) """, (b.origin, exchange.id, b.amount)) cursor.run("DELETE FROM cash_bundles WHERE id = %s", (b.id, )) x -= b.amount if x == 0: break else: assert x > 0 cursor.run( """ INSERT INTO e2e_transfers (origin, withdrawal, amount) VALUES (%s, %s, %s) """, (b.origin, exchange.id, x)) cursor.run( """ UPDATE cash_bundles SET amount = (amount - %s) WHERE id = %s """, (x, b.id)) break elif amount > 0 and exchange.amount < 0: cursor.run( """ LOCK TABLE cash_bundles IN EXCLUSIVE MODE; INSERT INTO cash_bundles (owner, origin, amount, ts) SELECT %(p_id)s, t.origin, t.amount, e.timestamp FROM e2e_transfers t JOIN exchanges e ON e.id = t.origin WHERE t.withdrawal = %(e_id)s; DELETE FROM e2e_transfers WHERE withdrawal = %(e_id)s; """, dict(p_id=participant.id, e_id=exchange.id)) elif amount > 0: cursor.run( """ INSERT INTO cash_bundles (owner, origin, amount, ts) VALUES (%s, %s, %s, %s) """, (participant.id, exchange.id, amount, exchange.timestamp)) participant.set_attributes(balance=new_balance) if amount != 0: participant.update_giving_and_tippees(cursor) merge_cash_bundles(cursor, participant.id)