From 4494f473a66bd3c876e3f64fbe7b10defded9c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 17 Feb 2023 18:03:31 +0100 Subject: [PATCH] remove lndhub (#1523) --- lnbits/extensions/lndhub/README.md | 6 - lnbits/extensions/lndhub/__init__.py | 27 -- lnbits/extensions/lndhub/config.json | 6 - lnbits/extensions/lndhub/decorators.py | 42 ---- lnbits/extensions/lndhub/migrations.py | 2 - .../extensions/lndhub/static/image/lndhub.png | Bin 32115 -> 0 bytes .../templates/lndhub/_instructions.html | 38 --- .../lndhub/templates/lndhub/_lndhub.html | 20 -- .../lndhub/templates/lndhub/index.html | 94 ------- lnbits/extensions/lndhub/utils.py | 19 -- lnbits/extensions/lndhub/views.py | 13 - lnbits/extensions/lndhub/views_api.py | 230 ------------------ 12 files changed, 497 deletions(-) delete mode 100644 lnbits/extensions/lndhub/README.md delete mode 100644 lnbits/extensions/lndhub/__init__.py delete mode 100644 lnbits/extensions/lndhub/config.json delete mode 100644 lnbits/extensions/lndhub/decorators.py delete mode 100644 lnbits/extensions/lndhub/migrations.py delete mode 100644 lnbits/extensions/lndhub/static/image/lndhub.png delete mode 100644 lnbits/extensions/lndhub/templates/lndhub/_instructions.html delete mode 100644 lnbits/extensions/lndhub/templates/lndhub/_lndhub.html delete mode 100644 lnbits/extensions/lndhub/templates/lndhub/index.html delete mode 100644 lnbits/extensions/lndhub/utils.py delete mode 100644 lnbits/extensions/lndhub/views.py delete mode 100644 lnbits/extensions/lndhub/views_api.py diff --git a/lnbits/extensions/lndhub/README.md b/lnbits/extensions/lndhub/README.md deleted file mode 100644 index ddd2020a..00000000 --- a/lnbits/extensions/lndhub/README.md +++ /dev/null @@ -1,6 +0,0 @@ -

lndhub Extension

-

*connect to your lnbits wallet from BlueWallet or Zeus*

- -Lndhub has nothing to do with lnd, it is just the name of the HTTP/JSON protocol https://bluewallet.io/ uses to talk to their Lightning custodian server at https://lndhub.io/. - -Despite not having been planned to this, Lndhub because somewhat a standard for custodian wallet communication when https://t.me/lntxbot and https://zeusln.app/ implemented the same interface. And with this extension LNbits joins the same club. diff --git a/lnbits/extensions/lndhub/__init__.py b/lnbits/extensions/lndhub/__init__.py deleted file mode 100644 index 344e91c6..00000000 --- a/lnbits/extensions/lndhub/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -from fastapi import APIRouter -from starlette.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer - -db = Database("ext_lndhub") - -lndhub_ext: APIRouter = APIRouter(prefix="/lndhub", tags=["lndhub"]) - -lndhub_static_files = [ - { - "path": "/lndhub/static", - "app": StaticFiles(directory="lnbits/extensions/lndhub/static"), - "name": "lndhub_static", - } -] - - -def lndhub_renderer(): - return template_renderer(["lnbits/extensions/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 diff --git a/lnbits/extensions/lndhub/config.json b/lnbits/extensions/lndhub/config.json deleted file mode 100644 index 30a2ce59..00000000 --- a/lnbits/extensions/lndhub/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "LndHub", - "short_description": "Access lnbits from BlueWallet or Zeus", - "tile": "/lndhub/static/image/lndhub.png", - "contributors": ["fiatjaf"] -} diff --git a/lnbits/extensions/lndhub/decorators.py b/lnbits/extensions/lndhub/decorators.py deleted file mode 100644 index 48118087..00000000 --- a/lnbits/extensions/lndhub/decorators.py +++ /dev/null @@ -1,42 +0,0 @@ -from base64 import b64decode - -from fastapi import Request, status -from fastapi.param_functions import Security -from fastapi.security.api_key import APIKeyHeader -from starlette.exceptions import HTTPException - -from lnbits.decorators import WalletTypeInfo, get_key_type - -api_key_header_auth = APIKeyHeader( - name="AUTHORIZATION", - auto_error=False, - description="Admin or Invoice key for LNDHub API's", -) - - -async def check_wallet( - r: Request, api_key_header_auth: str = Security(api_key_header_auth) -) -> WalletTypeInfo: - if not api_key_header_auth: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid auth key" - ) - - t = api_key_header_auth.split(" ")[1] - _, token = b64decode(t).decode().split(":") - - return await get_key_type(r, api_key_header=token) - - -async def require_admin_key( - r: Request, api_key_header_auth: str = Security(api_key_header_auth) -): - wallet = await check_wallet(r, api_key_header_auth) - if wallet.wallet_type != 0: - # If wallet type is not admin then return the unauthorized status - # This also covers when the user passes an invalid key type - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Admin key required." - ) - else: - return wallet diff --git a/lnbits/extensions/lndhub/migrations.py b/lnbits/extensions/lndhub/migrations.py deleted file mode 100644 index d6ea5fde..00000000 --- a/lnbits/extensions/lndhub/migrations.py +++ /dev/null @@ -1,2 +0,0 @@ -async def migrate(): - pass diff --git a/lnbits/extensions/lndhub/static/image/lndhub.png b/lnbits/extensions/lndhub/static/image/lndhub.png deleted file mode 100644 index f5e95a6e76861406d71ef01fc6d39a480d32ae81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32115 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 diff --git a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html b/lnbits/extensions/lndhub/templates/lndhub/_instructions.html deleted file mode 100644 index a5eba8a2..00000000 --- a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - To access an LNbits wallet from a mobile phone, -
    -
  1. - Install either - Zeus or - BlueWallet; -
  2. -
  3. - Go to Add a wallet / Import wallet on BlueWallet or - Settings / Add a new node on Zeus. -
  4. -
  5. Select the desired wallet on this page;
  6. -
  7. Scan one of the two QR codes from the mobile wallet.
  8. -
-
    -
  • - Invoice URLs mean the mobile wallet will only have the - authorization to read your payments and invoices and generate new - invoices. -
  • -
  • - Admin URLs mean the mobile wallet will be able to pay - invoices.. -
  • -
-
- -
-
diff --git a/lnbits/extensions/lndhub/templates/lndhub/_lndhub.html b/lnbits/extensions/lndhub/templates/lndhub/_lndhub.html deleted file mode 100644 index 73097dbf..00000000 --- a/lnbits/extensions/lndhub/templates/lndhub/_lndhub.html +++ /dev/null @@ -1,20 +0,0 @@ - - - -

- LndHub is a protocol invented by - BlueWallet - that allows mobile wallets to query payments and balances, generate - invoices and make payments from accounts that exist on a server. The - protocol is a collection of HTTP endpoints exposed through the internet. -

-

- For a wallet that supports it, reading a QR code that contains the URL - along with secret access credentials should enable access. Currently it - is supported by - Zeus and - BlueWallet. -

-
-
-
diff --git a/lnbits/extensions/lndhub/templates/lndhub/index.html b/lnbits/extensions/lndhub/templates/lndhub/index.html deleted file mode 100644 index fc666da9..00000000 --- a/lnbits/extensions/lndhub/templates/lndhub/index.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} {% raw %} -
-
-
- - - -
- Copy LndHub {{type}} URL -
-
-
-
- - - - - - - -
- - {% endraw %} - -
- - -
- {{SITE_TITLE}} LndHub extension -
-
- - - - {% include "lndhub/_instructions.html" %} - - {% include "lndhub/_lndhub.html" %} - - -
-
-
- -{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/lndhub/utils.py b/lnbits/extensions/lndhub/utils.py deleted file mode 100644 index 00865080..00000000 --- a/lnbits/extensions/lndhub/utils.py +++ /dev/null @@ -1,19 +0,0 @@ -from lnbits.bolt11 import Invoice - - -def to_buffer(payment_hash: str): - return {"type": "Buffer", "data": [b for b in bytes.fromhex(payment_hash)]} - - -def decoded_as_lndhub(invoice: Invoice): - return { - "destination": invoice.payee, - "payment_hash": invoice.payment_hash, - "num_satoshis": invoice.amount_msat / 1000, - "timestamp": str(invoice.date), - "expiry": str(invoice.expiry), - "description": invoice.description, - "fallback_addr": "", - "cltv_expiry": invoice.min_final_cltv_expiry, - "route_hints": "", - } diff --git a/lnbits/extensions/lndhub/views.py b/lnbits/extensions/lndhub/views.py deleted file mode 100644 index b216f8b1..00000000 --- a/lnbits/extensions/lndhub/views.py +++ /dev/null @@ -1,13 +0,0 @@ -from fastapi import Depends, Request - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import lndhub_ext, lndhub_renderer - - -@lndhub_ext.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()} - ) diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py deleted file mode 100644 index 059604f2..00000000 --- a/lnbits/extensions/lndhub/views_api.py +++ /dev/null @@ -1,230 +0,0 @@ -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 lnbits import bolt11 -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_wallet_class, settings - -from . import lndhub_ext -from .decorators import check_wallet, require_admin_key -from .utils import decoded_as_lndhub, to_buffer - - -@lndhub_ext.get("/ext/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): - token = ( - data.refresh_token - if data.refresh_token - else urlsafe_b64encode((data.login + ":" + data.password).encode()).decode( - "ascii" - ) - ) - return {"refresh_token": token, "access_token": token} - - -class AddInvoice(BaseModel): - amt: str = Query(...) - memo: str = Query(...) - preimage: str = Query(None) - - -@lndhub_ext.post("/ext/addinvoice") -async def lndhub_addinvoice( - data: AddInvoice, wallet: WalletTypeInfo = Depends(check_wallet) -): - try: - _, pr = await create_invoice( - wallet_id=wallet.wallet.id, - amount=int(data.amt), - memo=data.memo or settings.lnbits_site_title, - extra={"tag": "lndhub"}, - ) - except: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Failed to create invoice" - ) - 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, - } - - -class CreateInvoice(BaseModel): - invoice: str = Query(...) - - -@lndhub_ext.post("/ext/payinvoice") -async def lndhub_payinvoice( - r_invoice: CreateInvoice, wallet: WalletTypeInfo = Depends(require_admin_key) -): - try: - await pay_invoice( - wallet_id=wallet.wallet.id, - payment_request=r_invoice.invoice, - extra={"tag": "lndhub"}, - ) - except: - raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Payment failed") - - invoice: bolt11.Invoice = bolt11.decode(r_invoice.invoice) - - return { - "payment_error": "", - "payment_preimage": "0" * 64, - "route": {}, - "payment_hash": invoice.payment_hash, - "decoded": decoded_as_lndhub(invoice), - "fee_msat": 0, - "type": "paid_invoice", - "fee": 0, - "value": invoice.amount_msat / 1000, - "timestamp": int(time.time()), - "memo": invoice.description, - } - - -@lndhub_ext.get("/ext/balance") -async def lndhub_balance( - wallet: WalletTypeInfo = Depends(check_wallet), -): - return {"BTC": {"AvailableBalance": wallet.wallet.balance}} - - -@lndhub_ext.get("/ext/gettxs") -async def lndhub_gettxs( - wallet: WalletTypeInfo = Depends(check_wallet), - limit: int = Query(20, ge=1, le=20), - 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, - "payment_hash": payment.payment_hash, - "fee_msat": payment.fee * 1000, - "type": "paid_invoice", - "fee": payment.fee, - "value": int(payment.amount / 1000), - "timestamp": payment.time, - "memo": payment.memo if not payment.pending else "Payment in transition", - } - for payment in reversed( - ( - await get_payments( - wallet_id=wallet.wallet.id, - pending=True, - complete=True, - outgoing=True, - incoming=False, - limit=limit, - offset=offset, - ) - ) - ) - ] - - -@lndhub_ext.get("/ext/getuserinvoices") -async def lndhub_getuserinvoices( - wallet: WalletTypeInfo = Depends(check_wallet), - limit: int = Query(20, ge=1, le=20), - offset: int = Query(0, ge=0), -): - WALLET = get_wallet_class() - 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 WALLET.get_invoice_status(invoice.checking_id)).pending - ) - - return [ - { - "r_hash": to_buffer(invoice.payment_hash), - "payment_request": invoice.bolt11, - "add_index": "500", - "description": invoice.memo, - "payment_hash": invoice.payment_hash, - "ispaid": not invoice.pending, - "amt": int(invoice.amount / 1000), - "expire_time": int(time.time() + 1800), - "timestamp": invoice.time, - "type": "user_invoice", - } - for invoice in reversed( - ( - await get_payments( - wallet_id=wallet.wallet.id, - pending=True, - complete=True, - incoming=True, - outgoing=False, - limit=limit, - offset=offset, - ) - ) - ) - ] - - -@lndhub_ext.get("/ext/getbtc") -async def lndhub_getbtc(wallet: WalletTypeInfo = Depends(check_wallet)): - "load an address for incoming onchain btc" - return [] - - -@lndhub_ext.get("/ext/getpending") -async def lndhub_getpending(wallet: WalletTypeInfo = Depends(check_wallet)): - "pending onchain transactions" - return [] - - -@lndhub_ext.get("/ext/decodeinvoice") -async def lndhub_decodeinvoice(invoice: str = Query(None)): - inv = bolt11.decode(invoice) - return decoded_as_lndhub(inv) - - -@lndhub_ext.get("/ext/checkrouteinvoice") -async def lndhub_checkrouteinvoice(): - "not implemented on canonical lndhub"