mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-22 14:22:55 +01:00
overengineered async fix for /lnurlwallet internal hanging.
This commit is contained in:
parent
e0b8470d40
commit
211ac0391b
7 changed files with 92 additions and 76 deletions
1
Pipfile
1
Pipfile
|
@ -20,6 +20,7 @@ quart-cors = "*"
|
|||
quart-compress = "*"
|
||||
secure = "*"
|
||||
typing-extensions = "*"
|
||||
httpx = "*"
|
||||
|
||||
[dev-packages]
|
||||
black = "==20.8b1"
|
||||
|
|
|
@ -8,4 +8,6 @@ core_app: Blueprint = Blueprint(
|
|||
|
||||
from .views.api import * # noqa
|
||||
from .views.generic import * # noqa
|
||||
from .views.lnurl import * # noqa
|
||||
from .tasks import grab_app_for_later
|
||||
|
||||
core_app.record(grab_app_for_later)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import httpx
|
||||
from typing import Optional, Tuple, Dict
|
||||
from quart import g
|
||||
from lnurl import LnurlWithdrawResponse
|
||||
|
||||
try:
|
||||
from typing import TypedDict # type: ignore
|
||||
|
@ -94,7 +96,7 @@ def pay_invoice(
|
|||
|
||||
# do the balance check
|
||||
wallet = get_wallet(wallet_id)
|
||||
assert wallet, "invalid wallet id"
|
||||
assert wallet
|
||||
if wallet.balance_msat < 0:
|
||||
g.db.rollback()
|
||||
raise PermissionError("Insufficient balance.")
|
||||
|
@ -119,6 +121,24 @@ def pay_invoice(
|
|||
return invoice.payment_hash
|
||||
|
||||
|
||||
async def redeem_lnurl_withdraw(wallet_id: str, res: LnurlWithdrawResponse, memo: Optional[str] = None) -> None:
|
||||
if not memo:
|
||||
memo = res.default_description
|
||||
|
||||
_, payment_request = create_invoice(
|
||||
wallet_id=wallet_id,
|
||||
amount=res.max_sats,
|
||||
memo=memo,
|
||||
extra={"tag": "lnurlwallet"},
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.get(
|
||||
res.callback.base,
|
||||
params={**res.callback.query_params, **{"k1": res.k1, "pr": payment_request}},
|
||||
)
|
||||
|
||||
|
||||
def check_invoice_status(wallet_id: str, payment_hash: str) -> PaymentStatus:
|
||||
payment = get_wallet_payment(wallet_id, payment_hash)
|
||||
if not payment:
|
||||
|
|
33
lnbits/core/tasks.py
Normal file
33
lnbits/core/tasks.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import asyncio
|
||||
from typing import Optional, Awaitable
|
||||
from quart import Quart, Request, g
|
||||
from werkzeug.datastructures import Headers
|
||||
|
||||
from lnbits.db import open_db
|
||||
|
||||
main_app: Optional[Quart] = None
|
||||
|
||||
|
||||
def grab_app_for_later(state):
|
||||
global main_app
|
||||
main_app = state.app
|
||||
|
||||
|
||||
def run_on_pseudo_request(awaitable: Awaitable):
|
||||
async def run(awaitable):
|
||||
fk = Request(
|
||||
"GET",
|
||||
"http",
|
||||
"/background/pseudo",
|
||||
b"",
|
||||
Headers([("host", "lnbits.background")]),
|
||||
"",
|
||||
"1.1",
|
||||
send_push_promise=lambda x, h: None,
|
||||
)
|
||||
async with main_app.request_context(fk):
|
||||
g.db = open_db()
|
||||
await awaitable
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(run(awaitable))
|
|
@ -1,6 +1,8 @@
|
|||
from quart import g, abort, redirect, request, render_template, send_from_directory, url_for
|
||||
from http import HTTPStatus
|
||||
import httpx
|
||||
from os import path
|
||||
from http import HTTPStatus
|
||||
from quart import g, abort, redirect, request, render_template, send_from_directory, url_for
|
||||
from lnurl import LnurlResponse, LnurlWithdrawResponse, decode as decode_lnurl # type: ignore
|
||||
|
||||
from lnbits.core import core_app
|
||||
from lnbits.decorators import check_user_exists, validate_uuids
|
||||
|
@ -13,6 +15,8 @@ from ..crud import (
|
|||
create_wallet,
|
||||
delete_wallet,
|
||||
)
|
||||
from ..services import redeem_lnurl_withdraw
|
||||
from ..tasks import run_on_pseudo_request
|
||||
|
||||
|
||||
@core_app.route("/favicon.ico")
|
||||
|
@ -73,12 +77,11 @@ async def wallet():
|
|||
|
||||
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
|
||||
|
||||
if wallet_id not in user.wallet_ids:
|
||||
wallet = user.get_wallet(wallet_id)
|
||||
if not wallet:
|
||||
abort(HTTPStatus.FORBIDDEN, "Not your wallet.")
|
||||
|
||||
return await render_template(
|
||||
"core/wallet.html", user=user, wallet=user.get_wallet(wallet_id), service_fee=service_fee
|
||||
)
|
||||
return await render_template("core/wallet.html", user=user, wallet=wallet, service_fee=service_fee)
|
||||
|
||||
|
||||
@core_app.route("/deletewallet")
|
||||
|
@ -98,3 +101,28 @@ async def deletewallet():
|
|||
return redirect(url_for("core.wallet", usr=g.user.id, wal=user_wallet_ids[0]))
|
||||
|
||||
return redirect(url_for("core.home"))
|
||||
|
||||
|
||||
@core_app.route("/lnurlwallet")
|
||||
async def lnurlwallet():
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
lnurl = decode_lnurl(request.args.get("lightning"))
|
||||
r = await client.get(str(lnurl))
|
||||
withdraw_res = LnurlResponse.from_dict(r.json())
|
||||
|
||||
if not withdraw_res.ok:
|
||||
return f"Could not process lnurl-withdraw: {withdraw_res.error_msg}", HTTPStatus.BAD_REQUEST
|
||||
|
||||
if not isinstance(withdraw_res, LnurlWithdrawResponse):
|
||||
return f"Expected an lnurl-withdraw code, got {withdraw_res.tag}", HTTPStatus.BAD_REQUEST
|
||||
except Exception as exc:
|
||||
return f"Could not process lnurl-withdraw: {exc}", HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
account = create_account()
|
||||
user = get_user(account.id)
|
||||
wallet = create_wallet(user_id=user.id)
|
||||
|
||||
run_on_pseudo_request(redeem_lnurl_withdraw(wallet.id, withdraw_res, "LNbits initial funding: voucher redeem."))
|
||||
|
||||
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import requests
|
||||
|
||||
from quart import abort, redirect, request, url_for
|
||||
from http import HTTPStatus
|
||||
from time import sleep
|
||||
from lnurl import LnurlWithdrawResponse, handle as handle_lnurl # type: ignore
|
||||
from lnurl.exceptions import LnurlException # type: ignore
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.core import core_app
|
||||
from lnbits.settings import WALLET
|
||||
|
||||
from ..crud import create_account, get_user, create_wallet, create_payment
|
||||
|
||||
|
||||
@core_app.route("/lnurlwallet")
|
||||
async def lnurlwallet():
|
||||
memo = "LNbits LNURL funding"
|
||||
|
||||
try:
|
||||
withdraw_res = handle_lnurl(request.args.get("lightning"))
|
||||
if not withdraw_res.ok:
|
||||
abort(HTTPStatus.BAD_REQUEST, f"Could not process LNURL-withdraw: {withdraw_res.error_msg}")
|
||||
if not isinstance(withdraw_res, LnurlWithdrawResponse):
|
||||
abort(HTTPStatus.BAD_REQUEST, "Not a valid LNURL-withdraw.")
|
||||
except LnurlException:
|
||||
abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process LNURL-withdraw.")
|
||||
|
||||
try:
|
||||
ok, checking_id, payment_request, error_message = WALLET.create_invoice(withdraw_res.max_sats, memo)
|
||||
except Exception as e:
|
||||
ok, error_message = False, str(e)
|
||||
|
||||
if not ok:
|
||||
abort(HTTPStatus.INTERNAL_SERVER_ERROR, error_message)
|
||||
|
||||
r = requests.get(
|
||||
withdraw_res.callback.base,
|
||||
params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": payment_request}},
|
||||
)
|
||||
|
||||
if not r.ok:
|
||||
abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process LNURL-withdraw.")
|
||||
|
||||
inv = bolt11.decode(payment_request)
|
||||
|
||||
for i in range(10):
|
||||
invoice_status = WALLET.get_invoice_status(checking_id)
|
||||
sleep(i)
|
||||
if not invoice_status.paid:
|
||||
continue
|
||||
break
|
||||
|
||||
user = get_user(create_account().id)
|
||||
wallet = create_wallet(user_id=user.id)
|
||||
create_payment(
|
||||
wallet_id=wallet.id,
|
||||
checking_id=checking_id,
|
||||
amount=withdraw_res.max_sats * 1000,
|
||||
memo=memo,
|
||||
pending=invoice_status.pending,
|
||||
payment_request=payment_request,
|
||||
payment_hash=inv.payment_hash,
|
||||
)
|
||||
|
||||
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
|
|
@ -152,8 +152,6 @@ async def api_lnurl_multi_response(unique_hash, id_unique_hash):
|
|||
found = True
|
||||
else:
|
||||
usescsv += "," + x
|
||||
print(x)
|
||||
print("usescsv: " + usescsv)
|
||||
if not found:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue