Skip to content

Commit

Permalink
removed timestamp from models
Browse files Browse the repository at this point in the history
atm fixed!

added delete payment

removed double claim

fixed msat error

fixed double spend

Format
  • Loading branch information
arcbtc committed Oct 7, 2024
1 parent bcdafe4 commit e9ca7d4
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 76 deletions.
4 changes: 3 additions & 1 deletion crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
23 changes: 11 additions & 12 deletions helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,24 @@ 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 = (
await fiat_amount_as_satoshis(float(decrypted[1]) / 100, device.currency) * 1000
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,
Expand Down
2 changes: 0 additions & 2 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -46,7 +45,6 @@ class LnurldevicePayment(BaseModel):
payload: str
pin: int
sats: int
timestamp: str


class Lnurlencode(BaseModel):
Expand Down
18 changes: 9 additions & 9 deletions templates/lnurldevice/atm.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@
></q-tab>

<q-tab
:disable="boltz == 'False' || amount < 100000"
:disable="boltz == 'False' || amount < 50000"
name="onchain"
label="onchain"
@click="updateTabAndClear(val)"
>
<q-tooltip v-if="boltz == 'True' && amount < 100000"
>Amount is too small to send over onchain, needs to be 10000+
<q-tooltip v-if="boltz == 'True' && amount < 50000"
>Amount is too small to send over onchain, needs to be 50000+
sats</q-tooltip
>
<q-tooltip v-if="boltz == 'False'">Onchain not available</q-tooltip>
</q-tab>

<q-tab
:disable="boltz == 'False' || amount < 2000"
:disable="boltz == 'False' || amount < 10000"
name="liquid"
label="liquid"
@click="updateTabAndClear(val)"
>
<q-tooltip v-if="boltz == 'True' && amount < 2000"
>Amount is too small to send over liquid, needs to be 2000+
<q-tooltip v-if="boltz == 'True' && amount < 10000"
>Amount is too small to send over liquid, needs to be 10000+
sats</q-tooltip
>
<q-tooltip v-if="boltz == 'False'">Onchain not available</q-tooltip>
Expand Down Expand Up @@ -232,11 +232,11 @@ <h3 class="text-h5">
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() {
Expand All @@ -247,8 +247,8 @@ <h3 class="text-h5">
''
)
if (response.data) {
this.ln = ''
this.notifyUser('Payment should be with you shortly', 'positive')
this.connectWebsocket(payment_id)
}
} catch (error) {
this.notifyApiError(error)
Expand Down
44 changes: 33 additions & 11 deletions views_api.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,6 +30,7 @@
)
from .helpers import register_atm_payment
from .models import CreateLnurldevice, Lnurlencode
from loguru import logger

lnurldevice_api_router = APIRouter()

Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -246,20 +263,25 @@ 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)
async with httpx.AsyncClient() as client:
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)}
80 changes: 39 additions & 41 deletions views_lnurl.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
create_lnurldevicepayment,
get_lnurldevice,
get_lnurldevicepayment,
delete_atm_payment_link,
update_lnurldevicepayment,
)
from .helpers import register_atm_payment, xor_decrypt
Expand Down Expand Up @@ -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"}
Expand Down

0 comments on commit e9ca7d4

Please sign in to comment.