mirror of
https://github.com/lnbits/lnbits.git
synced 2025-09-19 03:57:29 +02:00
prevent pay_invoice from locking sqlite for the entire app.
This commit is contained in:
@@ -85,81 +85,81 @@ async def pay_invoice(
|
|||||||
description: str = "",
|
description: str = "",
|
||||||
conn: Optional[Connection] = None,
|
conn: Optional[Connection] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
async with (db.reuse_conn(conn) if conn else db.connect()) as conn:
|
temp_id = f"temp_{urlsafe_short_hash()}"
|
||||||
temp_id = f"temp_{urlsafe_short_hash()}"
|
internal_id = f"internal_{urlsafe_short_hash()}"
|
||||||
internal_id = f"internal_{urlsafe_short_hash()}"
|
|
||||||
|
|
||||||
invoice = bolt11.decode(payment_request)
|
invoice = bolt11.decode(payment_request)
|
||||||
if invoice.amount_msat == 0:
|
if invoice.amount_msat == 0:
|
||||||
raise ValueError("Amountless invoices not supported.")
|
raise ValueError("Amountless invoices not supported.")
|
||||||
if max_sat and invoice.amount_msat > max_sat * 1000:
|
if max_sat and invoice.amount_msat > max_sat * 1000:
|
||||||
raise ValueError("Amount in invoice is too high.")
|
raise ValueError("Amount in invoice is too high.")
|
||||||
|
|
||||||
# put all parameters that don't change here
|
# put all parameters that don't change here
|
||||||
PaymentKwargs = TypedDict(
|
PaymentKwargs = TypedDict(
|
||||||
"PaymentKwargs",
|
"PaymentKwargs",
|
||||||
{
|
{
|
||||||
"wallet_id": str,
|
"wallet_id": str,
|
||||||
"payment_request": str,
|
"payment_request": str,
|
||||||
"payment_hash": str,
|
"payment_hash": str,
|
||||||
"amount": int,
|
"amount": int,
|
||||||
"memo": str,
|
"memo": str,
|
||||||
"extra": Optional[Dict],
|
"extra": Optional[Dict],
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
payment_kwargs: PaymentKwargs = dict(
|
||||||
|
wallet_id=wallet_id,
|
||||||
|
payment_request=payment_request,
|
||||||
|
payment_hash=invoice.payment_hash,
|
||||||
|
amount=-invoice.amount_msat,
|
||||||
|
memo=description or invoice.description or "",
|
||||||
|
extra=extra,
|
||||||
|
)
|
||||||
|
|
||||||
|
# check_internal() returns the checking_id of the invoice we're waiting for
|
||||||
|
internal_checking_id = await check_internal(invoice.payment_hash, conn=conn)
|
||||||
|
if internal_checking_id:
|
||||||
|
# create a new payment from this wallet
|
||||||
|
await create_payment(
|
||||||
|
checking_id=internal_id,
|
||||||
|
fee=0,
|
||||||
|
pending=False,
|
||||||
|
conn=conn,
|
||||||
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
payment_kwargs: PaymentKwargs = dict(
|
else:
|
||||||
wallet_id=wallet_id,
|
# create a temporary payment here so we can check if
|
||||||
payment_request=payment_request,
|
# the balance is enough in the next step
|
||||||
payment_hash=invoice.payment_hash,
|
await create_payment(
|
||||||
amount=-invoice.amount_msat,
|
checking_id=temp_id,
|
||||||
memo=description or invoice.description or "",
|
fee=-fee_reserve(invoice.amount_msat),
|
||||||
extra=extra,
|
conn=conn,
|
||||||
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check_internal() returns the checking_id of the invoice we're waiting for
|
# do the balance check
|
||||||
internal_checking_id = await check_internal(invoice.payment_hash, conn=conn)
|
wallet = await get_wallet(wallet_id, conn=conn)
|
||||||
if internal_checking_id:
|
assert wallet
|
||||||
# create a new payment from this wallet
|
if wallet.balance_msat < 0:
|
||||||
await create_payment(
|
raise PermissionError("Insufficient balance.")
|
||||||
checking_id=internal_id,
|
|
||||||
fee=0,
|
|
||||||
pending=False,
|
|
||||||
conn=conn,
|
|
||||||
**payment_kwargs,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# create a temporary payment here so we can check if
|
|
||||||
# the balance is enough in the next step
|
|
||||||
await create_payment(
|
|
||||||
checking_id=temp_id,
|
|
||||||
fee=-fee_reserve(invoice.amount_msat),
|
|
||||||
conn=conn,
|
|
||||||
**payment_kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
# do the balance check
|
if internal_checking_id:
|
||||||
wallet = await get_wallet(wallet_id, conn=conn)
|
# mark the invoice from the other side as not pending anymore
|
||||||
assert wallet
|
# so the other side only has access to his new money when we are sure
|
||||||
if wallet.balance_msat < 0:
|
# the payer has enough to deduct from
|
||||||
raise PermissionError("Insufficient balance.")
|
await update_payment_status(
|
||||||
|
checking_id=internal_checking_id, pending=False, conn=conn
|
||||||
|
)
|
||||||
|
|
||||||
if internal_checking_id:
|
# notify receiver asynchronously
|
||||||
# mark the invoice from the other side as not pending anymore
|
|
||||||
# so the other side only has access to his new money when we are sure
|
|
||||||
# the payer has enough to deduct from
|
|
||||||
await update_payment_status(
|
|
||||||
checking_id=internal_checking_id, pending=False, conn=conn
|
|
||||||
)
|
|
||||||
|
|
||||||
# notify receiver asynchronously
|
from lnbits.tasks import internal_invoice_queue
|
||||||
|
|
||||||
from lnbits.tasks import internal_invoice_queue
|
await internal_invoice_queue.put(internal_checking_id)
|
||||||
|
else:
|
||||||
await internal_invoice_queue.put(internal_checking_id)
|
# actually pay the external invoice
|
||||||
else:
|
payment: PaymentResponse = await WALLET.pay_invoice(payment_request)
|
||||||
# actually pay the external invoice
|
if payment.checking_id:
|
||||||
payment: PaymentResponse = await WALLET.pay_invoice(payment_request)
|
async with (db.reuse_conn(conn) if conn else db.connect()) as conn:
|
||||||
if payment.checking_id:
|
|
||||||
await create_payment(
|
await create_payment(
|
||||||
checking_id=payment.checking_id,
|
checking_id=payment.checking_id,
|
||||||
fee=payment.fee_msat,
|
fee=payment.fee_msat,
|
||||||
@@ -169,13 +169,13 @@ async def pay_invoice(
|
|||||||
**payment_kwargs,
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
await delete_payment(temp_id, conn=conn)
|
await delete_payment(temp_id, conn=conn)
|
||||||
else:
|
else:
|
||||||
raise PaymentFailure(
|
raise PaymentFailure(
|
||||||
payment.error_message
|
payment.error_message
|
||||||
or "Payment failed, but backend didn't give us an error message."
|
or "Payment failed, but backend didn't give us an error message."
|
||||||
)
|
)
|
||||||
|
|
||||||
return invoice.payment_hash
|
return invoice.payment_hash
|
||||||
|
|
||||||
|
|
||||||
async def redeem_lnurl_withdraw(
|
async def redeem_lnurl_withdraw(
|
||||||
|
Reference in New Issue
Block a user