Skip to content

Commit

Permalink
refactor: prepare lndhub for next release (#32)
Browse files Browse the repository at this point in the history
- add pyproject toml for typing
- do not check or change status of payment
- do not access fundingsource
- general code quality
  • Loading branch information
dni committed Jul 24, 2024
1 parent fe0f680 commit 02d89d0
Show file tree
Hide file tree
Showing 9 changed files with 2,200 additions and 119 deletions.
16 changes: 4 additions & 12 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
from fastapi import APIRouter

from lnbits.db import Database
from lnbits.helpers import template_renderer
from .views_api import lndhub_api_router
from .views import lndhub_generic_router

db = Database("ext_lndhub")

lndhub_ext: APIRouter = APIRouter(prefix="/lndhub", tags=["lndhub"])

lndhub_static_files = [
{
"path": "/lndhub/static",
"name": "lndhub_static",
}
]


def lndhub_renderer():
return template_renderer(["lndhub/templates"])


from .decorators import * # noqa: F401,F403
from .utils import * # noqa: F401,F403
from .views import * # noqa: F401,F403
from .views_api import * # noqa: F401,F403
lndhub_ext.include_router(lndhub_generic_router)
lndhub_ext.include_router(lndhub_api_router)
6 changes: 3 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "LndHub",
"short_description": "Access lnbits from BlueWallet or Zeus",
"short_description": "Access LNbits from BlueWallet or Zeus",
"tile": "/lndhub/static/image/lndhub.png",
"contributors": ["fiatjaf"],
"min_lnbits_version": "0.12.6"
"contributors": ["fiatjaf", "dni"],
"min_lnbits_version": "0.12.11"
}
3 changes: 2 additions & 1 deletion decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from fastapi.security.api_key import APIKeyHeader
from starlette.exceptions import HTTPException

from lnbits.decorators import WalletTypeInfo, get_key_type
from lnbits.decorators import get_key_type
from lnbits.core.models import WalletTypeInfo

api_key_header_auth = APIKeyHeader(
name="Authorization",
Expand Down
18 changes: 18 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from fastapi import Query
from pydantic import BaseModel


class LndhubCreateInvoice(BaseModel):
invoice: str = Query(...)


class LndhubAddInvoice(BaseModel):
amt: int = Query(...)
memo: str = Query(...)
preimage: str = Query(None)


class LndhubAuthData(BaseModel):
login: str = Query(None)
password: str = Query(None)
refresh_token: str = Query(None)
2,101 changes: 2,101 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[tool.poetry]
name = "lndhub"
version = "0.5.0"
description = "LNbits LNDHub Extension"
authors = ["dni <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.9"
lnbits = "^0.12.7"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
10 changes: 5 additions & 5 deletions utils.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
from lnbits.bolt11 import Invoice
from bolt11 import Bolt11


def to_buffer(payment_hash: str):
return {"type": "Buffer", "data": [b for b in bytes.fromhex(payment_hash)]}


def decoded_as_lndhub(invoice: Invoice):
def decoded_as_lndhub(invoice: Bolt11):
return {
"destination": invoice.payee,
"payment_hash": invoice.payment_hash,
"num_satoshis": invoice.amount_msat / 1000,
"num_satoshis": invoice.amount_msat / 1000 if invoice.amount_msat else 0,
"timestamp": str(invoice.date),
"expiry": str(invoice.expiry),
"description": invoice.description,
"fallback_addr": "",
"fallback_addr": invoice.fallback.address if invoice.fallback else "",
"cltv_expiry": invoice.min_final_cltv_expiry,
"route_hints": "",
"route_hints": invoice.route_hints,
}
9 changes: 6 additions & 3 deletions views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from fastapi import Depends, Request
from fastapi import Depends, Request, APIRouter

from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.helpers import template_renderer

from . import lndhub_ext, lndhub_renderer
def lndhub_renderer():
return template_renderer(["lndhub/templates"])

lndhub_generic_router = APIRouter()

@lndhub_ext.get("/")
@lndhub_generic_router.get("/")
async def lndhub_index(request: Request, user: User = Depends(check_user_exists)):
return lndhub_renderer().TemplateResponse(
"lndhub/index.html", {"request": request, "user": user.dict()}
Expand Down
141 changes: 46 additions & 95 deletions views_api.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
import time
from base64 import urlsafe_b64encode
from http import HTTPStatus

from fastapi import Depends, Query
from pydantic import BaseModel
from starlette.exceptions import HTTPException
from fastapi import Depends, Query, APIRouter

from lnbits import bolt11
from bolt11 import decode as bolt11_decode
from lnbits.core.crud import get_payments
from lnbits.core.services import create_invoice, pay_invoice
from lnbits.decorators import WalletTypeInfo
from lnbits.settings import get_funding_source, settings
from lnbits.core.models import WalletTypeInfo
from lnbits.settings import settings

from . import lndhub_ext
from .decorators import check_wallet, require_admin_key
from .utils import decoded_as_lndhub, to_buffer
from .models import LndhubAuthData, LndhubCreateInvoice, LndhubAddInvoice

lndhub_api_router = APIRouter(prefix="/ext")

@lndhub_ext.get("/ext/getinfo")
@lndhub_api_router.get("/getinfo")
async def lndhub_getinfo():
return {"alias": settings.lnbits_site_title}


class AuthData(BaseModel):
login: str = Query(None)
password: str = Query(None)
refresh_token: str = Query(None)


@lndhub_ext.post("/ext/auth")
async def lndhub_auth(data: AuthData):
@lndhub_api_router.post("/auth")
async def lndhub_auth(data: LndhubAuthData):
token = (
data.refresh_token
if data.refresh_token
Expand All @@ -40,58 +32,44 @@ async def lndhub_auth(data: AuthData):
return {"refresh_token": token, "access_token": token}


class AddInvoice(BaseModel):
amt: int = Query(...)
memo: str = Query(...)
preimage: str = Query(None)


@lndhub_ext.post("/ext/addinvoice")
@lndhub_api_router.post("/addinvoice")
async def lndhub_addinvoice(
data: AddInvoice, wallet: WalletTypeInfo = Depends(check_wallet)
data: LndhubAddInvoice, wallet: WalletTypeInfo = Depends(check_wallet)
):
try:
_, pr = await create_invoice(
payment_hash, pr = await create_invoice(
wallet_id=wallet.wallet.id,
amount=data.amt,
memo=data.memo or settings.lnbits_site_title,
extra={"tag": "lndhub"},
)
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Failed to create invoice: {str(exc)}"
)
return {"error": f"Failed to create invoice: {exc!s}"}

invoice = bolt11.decode(pr)
return {
"pay_req": pr,
"payment_request": pr,
"add_index": "500",
"r_hash": to_buffer(invoice.payment_hash),
"hash": invoice.payment_hash,
"r_hash": to_buffer(payment_hash),
"hash": payment_hash,
}


class CreateInvoice(BaseModel):
invoice: str = Query(...)


@lndhub_ext.post("/ext/payinvoice")
@lndhub_api_router.post("/payinvoice")
async def lndhub_payinvoice(
r_invoice: CreateInvoice, wallet: WalletTypeInfo = Depends(require_admin_key)
r_invoice: LndhubCreateInvoice,
key_type: WalletTypeInfo = Depends(require_admin_key),
):
try:
invoice = bolt11_decode(r_invoice.invoice)
await pay_invoice(
wallet_id=wallet.wallet.id,
wallet_id=key_type.wallet.id,
payment_request=r_invoice.invoice,
extra={"tag": "lndhub"},
)
except Exception:
return {"error": "Payment failed"}

invoice: bolt11.Invoice = bolt11.decode(r_invoice.invoice)

return {
"payment_error": "",
"payment_preimage": "0" * 64,
Expand All @@ -101,37 +79,25 @@ async def lndhub_payinvoice(
"fee_msat": 0,
"type": "paid_invoice",
"fee": 0,
"value": invoice.amount_msat / 1000,
"value": invoice.amount_msat / 1000 if invoice.amount_msat else 0,
"timestamp": int(time.time()),
"memo": invoice.description,
}


@lndhub_ext.get("/ext/balance")
@lndhub_api_router.get("/balance")
async def lndhub_balance(
wallet: WalletTypeInfo = Depends(check_wallet),
key_type: WalletTypeInfo = Depends(check_wallet),
):
return {"BTC": {"AvailableBalance": wallet.wallet.balance}}
return {"BTC": {"AvailableBalance": key_type.wallet.balance}}


@lndhub_ext.get("/ext/gettxs")
@lndhub_api_router.get("/gettxs")
async def lndhub_gettxs(
wallet: WalletTypeInfo = Depends(check_wallet),
key_type: WalletTypeInfo = Depends(check_wallet),
limit: int = Query(20, ge=1, le=200),
offset: int = Query(0, ge=0),
):
for payment in await get_payments(
wallet_id=wallet.wallet.id,
complete=False,
pending=True,
outgoing=True,
incoming=False,
limit=limit,
offset=offset,
exclude_uncheckable=True,
):
await payment.check_status()

return [
{
"payment_preimage": payment.preimage,
Expand All @@ -146,7 +112,7 @@ async def lndhub_gettxs(
for payment in reversed(
(
await get_payments(
wallet_id=wallet.wallet.id,
wallet_id=key_type.wallet.id,
pending=True,
complete=True,
outgoing=True,
Expand All @@ -159,44 +125,29 @@ async def lndhub_gettxs(
]


@lndhub_ext.get("/ext/getuserinvoices")
@lndhub_api_router.get("/getuserinvoices")
async def lndhub_getuserinvoices(
wallet: WalletTypeInfo = Depends(check_wallet),
key_type: WalletTypeInfo = Depends(check_wallet),
limit: int = Query(20, ge=1, le=200),
offset: int = Query(0, ge=0),
):
funding_source = get_funding_source()
for invoice in await get_payments(
wallet_id=wallet.wallet.id,
complete=False,
pending=True,
outgoing=False,
incoming=True,
limit=limit,
offset=offset,
exclude_uncheckable=True,
):
await invoice.set_pending(
(await funding_source.get_invoice_status(invoice.checking_id)).pending
)

return [
{
"r_hash": to_buffer(invoice.payment_hash),
"payment_request": invoice.bolt11,
"r_hash": to_buffer(payment.payment_hash),
"payment_request": payment.bolt11,
"add_index": "500",
"description": invoice.extra.get("comment") or invoice.memo,
"payment_hash": invoice.payment_hash,
"ispaid": not invoice.pending,
"amt": int(invoice.amount / 1000),
"description": payment.extra.get("comment") or payment.memo,
"payment_hash": payment.payment_hash,
"ispaid": payment.success,
"amt": int(payment.amount / 1000),
"expire_time": int(time.time() + 1800),
"timestamp": invoice.time,
"timestamp": payment.time,
"type": "user_invoice",
}
for invoice in reversed(
for payment in reversed(
(
await get_payments(
wallet_id=wallet.wallet.id,
wallet_id=key_type.wallet.id,
pending=True,
complete=True,
incoming=True,
Expand All @@ -209,24 +160,24 @@ async def lndhub_getuserinvoices(
]


@lndhub_ext.get("/ext/getbtc")
async def lndhub_getbtc(wallet: WalletTypeInfo = Depends(check_wallet)):
@lndhub_api_router.get("/getbtc", dependencies=[Depends(check_wallet)])
async def lndhub_getbtc():
"load an address for incoming onchain btc"
return []


@lndhub_ext.get("/ext/getpending")
async def lndhub_getpending(wallet: WalletTypeInfo = Depends(check_wallet)):
@lndhub_api_router.get("/getpending", dependencies=[Depends(check_wallet)])
async def lndhub_getpending():
"pending onchain transactions"
return []


@lndhub_ext.get("/ext/decodeinvoice")
async def lndhub_decodeinvoice(invoice: str = Query(None)):
inv = bolt11.decode(invoice)
@lndhub_api_router.get("/decodeinvoice")
async def lndhub_decodeinvoice(invoice: str):
inv = bolt11_decode(invoice)
return decoded_as_lndhub(inv)


@lndhub_ext.get("/ext/checkrouteinvoice")
@lndhub_api_router.get("/checkrouteinvoice")
async def lndhub_checkrouteinvoice():
"not implemented on canonical lndhub"

0 comments on commit 02d89d0

Please sign in to comment.