From e9ca7d49ed70f67cce3482a682629015b2eea859 Mon Sep 17 00:00:00 2001 From: Arc Date: Mon, 7 Oct 2024 15:27:09 +0100 Subject: [PATCH] removed timestamp from models atm fixed! added delete payment removed double claim fixed msat error fixed double spend Format --- crud.py | 4 +- helpers.py | 23 +++++----- models.py | 2 - templates/lnurldevice/atm.html | 18 ++++---- views_api.py | 44 ++++++++++++++----- views_lnurl.py | 80 +++++++++++++++++----------------- 6 files changed, 95 insertions(+), 76 deletions(-) diff --git a/crud.py b/crud.py index 202f0c8..af52485 100644 --- a/crud.py +++ b/crud.py @@ -216,7 +216,9 @@ async def get_lnurldevicepayment( async def get_lnurldevicepayments( lnurldevice_ids: List[str], -) -> List[LnurldevicePayment]: +) -> Optional[List[LnurldevicePayment]]: + if not lnurldevice_ids: + return [] q = ",".join(["?"] * len(lnurldevice_ids)) rows = await db.fetchall( f""" diff --git a/helpers.py b/helpers.py index 31eb92f..fdc8f96 100644 --- a/helpers.py +++ b/helpers.py @@ -16,17 +16,7 @@ async def register_atm_payment( """ Register an ATM payment to avoid double pull. """ - lnurldevicepayment = await get_recent_lnurldevicepayment(payload) - # If the payment is already registered and been paid, return None - if lnurldevicepayment and lnurldevicepayment.payload == lnurldevicepayment.payhash: - return None, None - # If the payment is already registered and not been paid, return lnurlpayment record - elif ( - lnurldevicepayment and lnurldevicepayment.payload != lnurldevicepayment.payhash - ): - return lnurldevicepayment, None - - # else create a new lnurlpayment record + # create a new lnurlpayment record data = base64.urlsafe_b64decode(payload) decrypted = xor_decrypt(device.key.encode(), data) price_msat = ( @@ -34,7 +24,16 @@ async def register_atm_payment( if device.currency != "sat" else decrypted[1] * 1000 ) - price_msat = int(price_msat * ((device.profit / 100) + 1)) + price_msat = int(price_msat - ((price_msat / 100) * device.profit)) + + lnurldevicepayment = await get_recent_lnurldevicepayment(payload) + # If the payment is already registered and been paid, return None + if lnurldevicepayment and lnurldevicepayment.payload == lnurldevicepayment.payhash: + return None, price_msat + # If the payment is already registered and not been paid, return lnurlpayment record + if lnurldevicepayment and lnurldevicepayment.payload != lnurldevicepayment.payhash: + return lnurldevicepayment, price_msat + lnurldevicepayment = await create_lnurldevicepayment( deviceid=device.id, payload=payload, diff --git a/models.py b/models.py index ff6d6ab..b280a7a 100644 --- a/models.py +++ b/models.py @@ -32,7 +32,6 @@ class Lnurldevice(BaseModel): currency: str device: str extra: Optional[Union[Json[List[LnurldeviceExtra]], str]] - timestamp: str @property def lnurlpay_metadata(self) -> LnurlPayMetadata: @@ -46,7 +45,6 @@ class LnurldevicePayment(BaseModel): payload: str pin: int sats: int - timestamp: str class Lnurlencode(BaseModel): diff --git a/templates/lnurldevice/atm.html b/templates/lnurldevice/atm.html index a0e80b1..c7fbedb 100644 --- a/templates/lnurldevice/atm.html +++ b/templates/lnurldevice/atm.html @@ -29,26 +29,26 @@ > - Amount is too small to send over onchain, needs to be 10000+ + Amount is too small to send over onchain, needs to be 50000+ sats Onchain not available - Amount is too small to send over liquid, needs to be 2000+ + Amount is too small to send over liquid, needs to be 10000+ sats Onchain not available @@ -232,11 +232,11 @@

this.tab = val.name }, sendOnchainAddress() { - this.onchain_liquid = 'BTC-BTC' + this.onchain_liquid = 'BTCtempBTC' this.sendAddress() }, sendLiquidAddress() { - this.onchain_liquid = 'L-BTC-BTC' + this.onchain_liquid = 'L-BTCtempBTC' this.sendAddress() }, async sendAddress() { @@ -247,8 +247,8 @@

'' ) if (response.data) { + this.ln = '' this.notifyUser('Payment should be with you shortly', 'positive') - this.connectWebsocket(payment_id) } } catch (error) { this.notifyApiError(error) diff --git a/views_api.py b/views_api.py index 38e1c68..e312ef7 100644 --- a/views_api.py +++ b/views_api.py @@ -1,5 +1,6 @@ from http import HTTPStatus +import bolt11 import httpx from fastapi import APIRouter, Depends, HTTPException, Request from lnbits.core.crud import get_user, get_wallet @@ -29,6 +30,7 @@ ) from .helpers import register_atm_payment from .models import CreateLnurldevice, Lnurlencode +from loguru import logger lnurldevice_api_router = APIRouter() @@ -158,12 +160,6 @@ async def get_lnurldevice_payment_lightning( status_code=HTTPStatus.NOT_FOUND, detail="Payment already claimed." ) - # If its an invoice check its a legit invoice - if ln[:4] == "lnbc": - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Invoices not supported." - ) - # If its an lnaddress or lnurlp get the request from callback elif ln[:5] == "lnurl" or "@" in ln and "." in ln.split("@")[-1]: data = await api_lnurlscan(ln) @@ -183,6 +179,10 @@ async def get_lnurldevice_payment_lightning( ) ln = response.json()["pr"] + # If just an invoice + elif ln[:4] == "lnbc": + ln = ln + # If ln is gibberish, return an error else: raise HTTPException( @@ -193,6 +193,23 @@ async def get_lnurldevice_payment_lightning( """, ) + # If its an invoice check its a legit invoice + if ln[:4] == "lnbc": + invoice = bolt11.decode(ln) + if not invoice.payment_hash: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not valid payment request" + ) + if not invoice.payment_hash: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not valid payment request" + ) + if int(invoice.amount_msat / 1000) != lnurldevicepayment.sats: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Request is not the same as withdraw amount", + ) + # Finally log the payment and make the payment try: lnurldevicepayment, price_msat = await register_atm_payment(lnurldevice, p) @@ -215,7 +232,7 @@ async def get_lnurldevice_payment_lightning( @lnurldevice_api_router.get( - "'/api/v1/boltz/{lnurldevice_id}/{payload}/{onchain_liquid}/{address}" + "/api/v1/boltz/{lnurldevice_id}/{payload}/{onchain_liquid}/{address}" ) async def get_lnurldevice_payment_boltz( req: Request, lnurldevice_id: str, payload: str, onchain_liquid: str, address: str @@ -246,11 +263,15 @@ async def get_lnurldevice_payment_boltz( data = { "wallet": lnurldevice.wallet, - "asset": onchain_liquid.replace("-", "/"), - "amount": price_msat, + "asset": onchain_liquid.replace("temp", "/"), + "amount": lnurldevicepayment.sats, + "direction": "send", "instant_settlement": True, "onchain_address": address, + "feerate": False, + "feerate_value": 0, } + try: lnurldevicepayment.payload = payload await update_lnurldevicepayment(lnurldevicepayment) @@ -258,8 +279,9 @@ async def get_lnurldevice_payment_boltz( response = await client.post( url=f"http://{settings.host}:{settings.port}/boltz/api/v1/swap/reverse", headers={"X-API-KEY": wallet.adminkey}, - data=data, + json=data, ) - return response.json() + resp = response.json() + return resp except Exception as exc: return {"status": "ERROR", "reason": str(exc)} diff --git a/views_lnurl.py b/views_lnurl.py index 3453983..9d0454d 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -12,6 +12,7 @@ create_lnurldevicepayment, get_lnurldevice, get_lnurldevicepayment, + delete_atm_payment_link, update_lnurldevicepayment, ) from .helpers import register_atm_payment, xor_decrypt @@ -199,59 +200,56 @@ async def lnurl_callback( ): lnurldevicepayment = await get_lnurldevicepayment(paymentid) if not lnurldevicepayment: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="lnurldevicepayment not found." - ) + return {"status": "ERROR", "reason": "lnurldevicepayment not found."} device = await get_lnurldevice(lnurldevicepayment.deviceid, request) if not device: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="lnurldevice not found." - ) + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "lnurldevice not found."} if device.device == "atm": if lnurldevicepayment.payload == lnurldevicepayment.payhash: + await delete_atm_payment_link(paymentid) return {"status": "ERROR", "reason": "Payment already claimed"} if not pr: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="No payment request" - ) + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "No payment request."} invoice = bolt11.decode(pr) if not invoice.payment_hash: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not valid payment request" - ) + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "Not valid payment request."} + if not invoice.payment_hash: + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "Not valid payment request."} wallet = await get_wallet(device.wallet) assert wallet if wallet.balance_msat < (int(lnurldevicepayment.sats / 1000) + 100): - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not enough funds" + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "Not enough funds."} + if lnurldevicepayment.payload != k1: + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "Bad K1"} + if lnurldevicepayment.payhash != "payment_hash": + await delete_atm_payment_link(paymentid) + return {"status": "ERROR", "reason": "Payment already claimed"} + try: + lnurldevicepayment.payhash = lnurldevicepayment.payload + lnurldevicepayment_updated = await update_lnurldevicepayment( + lnurldevicepayment ) - else: - if lnurldevicepayment.payload != k1: - return {"status": "ERROR", "reason": "Bad K1"} - if lnurldevicepayment.payhash != "payment_hash": - return {"status": "ERROR", "reason": "Payment already claimed"} - try: - lnurldevicepayment.payhash = lnurldevicepayment.payload - lnurldevicepayment_updated = await update_lnurldevicepayment( - lnurldevicepayment - ) - assert lnurldevicepayment_updated - await pay_invoice( - wallet_id=device.wallet, - payment_request=pr, - max_sat=int(lnurldevicepayment_updated.sats / 1000), - extra={"tag": "withdraw"}, - ) - except Exception as exc: - lnurldevicepayment.payhash = "payment_hash" - lnurldevicepayment_updated = await update_lnurldevicepayment( - lnurldevicepayment - ) - assert lnurldevicepayment_updated - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Failed to make payment" - ) from exc - return {"status": "OK"} + assert lnurldevicepayment_updated + await pay_invoice( + wallet_id=device.wallet, + payment_request=pr, + max_sat=int(lnurldevicepayment_updated.sats / 1000), + extra={"tag": "lnurldevice_withdraw"}, + ) + except: + lnurldevicepayment.payhash = "payment_hash" + lnurldevicepayment_updated = await update_lnurldevicepayment( + lnurldevicepayment + ) + assert lnurldevicepayment_updated + return {"status": "ERROR", "reason": "Failed to make payment."} + return {"status": "OK"} if device.device == "switch": if not amount: return {"status": "ERROR", "reason": "No amount"}