From 9185342c725b2ae6c4165641b2a889eb630b59bc Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 8 Oct 2020 16:03:18 -0300 Subject: [PATCH] simplify environment variables required. instead of multiple keys/macaroons with different permissions we request only one. if someone wants to use lnbits with an invoice macaroon they're free to do it and we will just fail on 'pay' methods, as before. this also grandfathers the previous environment variables names so everything keeps working without people having to change their setups. in the meantime some bugs with lntxbot and c-lightning were fixed and the `requests` dependency was eliminated because I can't organize myself into meaningful chunks of changes. --- .env.example | 18 ++---- Pipfile | 1 - Pipfile.lock | 55 +++++++----------- lnbits/core/views/api.py | 7 ++- lnbits/extensions/amilk/views_api.py | 6 +- lnbits/extensions/diagonalley/crud.py | 6 +- lnbits/extensions/example/views_api.py | 4 +- lnbits/wallets/clightning.py | 2 +- lnbits/wallets/lnbits.py | 37 +++++++------ lnbits/wallets/lndgrpc.py | 16 ++++-- lnbits/wallets/lndrest.py | 35 ++++++------ lnbits/wallets/lnpay.py | 16 +++--- lnbits/wallets/lntxbot.py | 77 ++++++++++++++------------ lnbits/wallets/opennode.py | 11 ++-- requirements.txt | 11 ++-- 15 files changed, 145 insertions(+), 157 deletions(-) diff --git a/.env.example b/.env.example index eedf2488..9a47cd7e 100644 --- a/.env.example +++ b/.env.example @@ -29,34 +29,28 @@ CLIGHTNING_RPC="/home/bob/.lightning/bitcoin/lightning-rpc" # LnbitsWallet LNBITS_ENDPOINT=http://127.0.0.1:5000 -LNBITS_INVOICE_KEY=LNBITS_INVOICE_KEY -LNBITS_ADMIN_KEY=LNBITS_ADMIN_KEY +LNBITS_KEY=LNBITS_ADMIN_KEY # LndWallet LND_GRPC_ENDPOINT=127.0.0.1 LND_GRPC_PORT=11009 LND_GRPC_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert" -LND_GRPC_ADMIN_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon" -LND_GRPC_INVOICE_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/invoice.macaroon" +LND_GRPC_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon" # LndRestWallet LND_REST_ENDPOINT=https://127.0.0.1:8080/ LND_REST_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert" -LND_REST_ADMIN_MACAROON="HEXSTRING" -LND_REST_INVOICE_MACAROON="HEXSTRING" +LND_REST_MACAROON="HEXSTRING" # LNPayWallet LNPAY_API_ENDPOINT=https://lnpay.co/v1/ LNPAY_API_KEY=LNPAY_API_KEY -LNPAY_ADMIN_KEY=LNPAY_ADMIN_KEY -LNPAY_INVOICE_KEY=LNPAY_INVOICE_KEY +LNPAY_WALLET_KEY=LNPAY_ADMIN_KEY # LntxbotWallet LNTXBOT_API_ENDPOINT=https://lntxbot.bigsun.xyz/ -LNTXBOT_ADMIN_KEY=LNTXBOT_ADMIN_KEY -LNTXBOT_INVOICE_KEY=LNTXBOT_INVOICE_KEY +LNTXBOT_KEY=LNTXBOT_ADMIN_KEY # OpenNodeWallet OPENNODE_API_ENDPOINT=https://api.opennode.com/ -OPENNODE_ADMIN_KEY=OPENNODE_ADMIN_KEY -OPENNODE_INVOICE_KEY=OPENNODE_INVOICE_KEY +OPENNODE_KEY=OPENNODE_ADMIN_KEY diff --git a/Pipfile b/Pipfile index ca5cfe07..0dbc6263 100644 --- a/Pipfile +++ b/Pipfile @@ -13,7 +13,6 @@ ecdsa = "*" environs = "*" lnurl = "*" pyscss = "*" -requests = "*" shortuuid = "*" quart = "*" quart-cors = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 2358d8ad..0146f905 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "76a3823f58d720ea680fdcd246f2a8b5fa16ce0a87a650e5e9fff5559dca7309" + "sha256": "193ef930ac0906127fd292b2fc7ba5b4d786227b6795d182dad9b57448fca75e" }, "pipfile-spec": 6, "requires": { @@ -106,13 +106,6 @@ ], "version": "==2020.6.20" }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, "click": { "hashes": [ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", @@ -139,10 +132,10 @@ }, "h11": { "hashes": [ - "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1", - "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1" + "sha256:3c6c61d69c6f13d41f1b80ab0322f1872702a3ba26e12aa864c928f6a43fbaab", + "sha256:ab6c335e1b6ef34b205d5ca3e228c9299cc7218b049819ec84a388c2525e5d87" ], - "version": "==0.9.0" + "version": "==0.11.0" }, "h2": { "hashes": [ @@ -162,30 +155,30 @@ }, "httpcore": { "hashes": [ - "sha256:72cfaa461dbdc262943ff4c9abf5b195391a03cdcc152e636adb4239b15e77e1", - "sha256:a35dddd1f4cc34ff37788337ef507c0ad0276241ece6daf663ac9e77c0b87232" + "sha256:18c4afcbfe884b635e59739105aed1692e132bc5d31597109f3c1c97e4ec1cac", + "sha256:2526a38f31ac5967d38b7f593b5d8c4bd3fa82c21400402f866ba3312946acbf" ], "markers": "python_version >= '3.6'", - "version": "==0.11.1" + "version": "==0.12.0" }, "httpx": { "hashes": [ - "sha256:02326f2d3c61133db31e4b88dd3432479b434e52a68d813eab6db930f13611ea", - "sha256:254b371e3880a8e2387bf9ead6949bac797bd557fda26eba19a6153a0c06bd2b" + "sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537", + "sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b" ], "index": "pypi", - "version": "==0.15.5" + "version": "==0.16.1" }, "hypercorn": { "extras": [ "trio" ], "hashes": [ - "sha256:6540faeba9dd44f7e74c7cc1beae3a438a7efb5f77323d1199457da46d32c2c2", - "sha256:b5c479023757e279f954b46a4ec9dd85e58a2bcbf4d959d5601cbced593e711d" + "sha256:81c69dd84a87b8e8b3ebf06ef5dd92836a8238f0ac65ded3d86befb8ba9acfeb", + "sha256:e3f317d6d64d15ce589f49e4f5057947259fa35332d169e62cb060e9997189e4" ], "index": "pypi", - "version": "==0.11.0" + "version": "==0.11.1" }, "hyperframe": { "hashes": [ @@ -356,14 +349,6 @@ "index": "pypi", "version": "==0.5.1" }, - "requests": { - "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" - ], - "index": "pypi", - "version": "==2.24.0" - }, "rfc3986": { "extras": [ "idna2008" @@ -437,14 +422,6 @@ "index": "pypi", "version": "==3.7.4.3" }, - "urllib3": { - "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.10" - }, "werkzeug": { "hashes": [ "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", @@ -657,12 +634,17 @@ "hashes": [ "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef", "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c", + "sha256:3d20024a70b97b4f9546696cbf2fd30bae5f42229fbddf8661261b1eaff0deb7", "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b", "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c", "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63", + "sha256:49f23ebd5ac073765ecbcf046edc10d63dcab2f4ae2bce160982cb30df0c0302", "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc", + "sha256:5d892a4f1c999834eaa3c32bc9e8b976c5825116cde553928c4c8e7e48ebda67", "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be", "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab", + "sha256:816064fc915796ea1f26966163f6845de5af78923dfcecf6551e095f00983650", + "sha256:84cada8effefe9a9f53f9b0d2ba9b7b6f5edf8d2155f9fdbe34616e06ececf81", "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19", "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637", "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc", @@ -670,6 +652,7 @@ "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d", "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b", "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100", + "sha256:c9443124c67b1515e4fe0bb0aa18df640965e1030f468a2a5dc2589b26d130ad", "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3", "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121", "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b", diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index bd198307..2f85c7f6 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -1,5 +1,6 @@ import trio # type: ignore import json +import traceback from quart import g, jsonify, request, make_response from http import HTTPStatus from binascii import unhexlify @@ -87,10 +88,10 @@ async def api_payments_pay_invoice(): return jsonify({"message": str(e)}), HTTPStatus.BAD_REQUEST except PermissionError as e: return jsonify({"message": str(e)}), HTTPStatus.FORBIDDEN - except Exception as e: - print(e) + except Exception as exc: + traceback.print_exc(7) g.db.rollback() - return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR + return jsonify({"message": str(exc)}), HTTPStatus.INTERNAL_SERVER_ERROR return ( jsonify( diff --git a/lnbits/extensions/amilk/views_api.py b/lnbits/extensions/amilk/views_api.py index c61a4fc1..2ad85c3a 100644 --- a/lnbits/extensions/amilk/views_api.py +++ b/lnbits/extensions/amilk/views_api.py @@ -1,4 +1,4 @@ -import requests +import httpx from quart import g, jsonify, request, abort from http import HTTPStatus from lnurl import LnurlWithdrawResponse, handle as handle_lnurl @@ -38,12 +38,12 @@ async def api_amilkit(amilk_id): wallet_id=milk.wallet, amount=withdraw_res.max_sats, memo=memo, extra={"tag": "amilk"} ) - r = requests.get( + r = httpx.get( withdraw_res.callback.base, params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": payment_request}}, ) - if not r.ok: + if r.is_error: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.") for i in range(10): diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py index ff182d96..a7ef5822 100644 --- a/lnbits/extensions/diagonalley/crud.py +++ b/lnbits/extensions/diagonalley/crud.py @@ -1,7 +1,7 @@ from base64 import urlsafe_b64encode from uuid import uuid4 from typing import List, Optional, Union -import requests +import httpx from lnbits.db import open_ext_db from lnbits.settings import WALLET from .models import Products, Orders, Indexers @@ -120,7 +120,7 @@ def get_diagonalleys_indexer(indexer_id: str) -> Optional[Indexers]: with open_ext_db("diagonalley") as db: roww = db.fetchone("SELECT * FROM indexers WHERE id = ?", (indexer_id,)) try: - x = requests.get(roww["indexeraddress"] + "/" + roww["ratingkey"]) + x = httpx.get(roww["indexeraddress"] + "/" + roww["ratingkey"]) if x.status_code == 200: print(x) print("poo") @@ -158,7 +158,7 @@ def get_diagonalleys_indexers(wallet_ids: Union[str, List[str]]) -> List[Indexer for r in rows: try: - x = requests.get(r["indexeraddress"] + "/" + r["ratingkey"]) + x = httpx.get(r["indexeraddress"] + "/" + r["ratingkey"]) if x.status_code == 200: with open_ext_db("diagonalley") as db: db.execute( diff --git a/lnbits/extensions/example/views_api.py b/lnbits/extensions/example/views_api.py index e3296f9e..095ddce2 100644 --- a/lnbits/extensions/example/views_api.py +++ b/lnbits/extensions/example/views_api.py @@ -3,7 +3,9 @@ # add your dependencies here # import json -# import requests +# import httpx +# (use httpx just like requests, except instead of response.ok there's only the +# response.is_error that is its inverse) from quart import jsonify from http import HTTPStatus diff --git a/lnbits/wallets/clightning.py b/lnbits/wallets/clightning.py index 852db352..5444d8db 100644 --- a/lnbits/wallets/clightning.py +++ b/lnbits/wallets/clightning.py @@ -73,7 +73,7 @@ class CLightningWallet(Wallet): raise KeyError("supplied an invalid checking_id") def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = self.ln.listpays(payment_hash=checking_id) + r = self.ln.call("listpays", {"payment_hash": checking_id}) if not r["pays"]: return PaymentStatus(False) if r["pays"][0]["payment_hash"] == checking_id: diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index 4a470fe0..f014bf45 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -1,7 +1,7 @@ import trio # type: ignore +import httpx from os import getenv from typing import Optional, Dict, AsyncGenerator -from requests import get, post from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet @@ -11,8 +11,9 @@ class LNbitsWallet(Wallet): def __init__(self): self.endpoint = getenv("LNBITS_ENDPOINT") - self.auth_admin = {"X-Api-Key": getenv("LNBITS_ADMIN_KEY")} - self.auth_invoice = {"X-Api-Key": getenv("LNBITS_INVOICE_KEY")} + + key = getenv("LNBITS_KEY") or getenv("LNBITS_ADMIN_KEY") or getenv("LNBITS_INVOICE_KEY") + self.key = {"X-Api-Key": key} def create_invoice( self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None @@ -23,45 +24,45 @@ class LNbitsWallet(Wallet): else: data["memo"] = memo or "" - r = post( + r = httpx.post( url=f"{self.endpoint}/api/v1/payments", - headers=self.auth_invoice, + headers=self.key, json=data, ) - ok, checking_id, payment_request, error_message = r.ok, None, None, None + ok, checking_id, payment_request, error_message = not r.is_error, None, None, None - if r.ok: + if r.is_error: + error_message = r.json()["message"] + else: data = r.json() checking_id, payment_request = data["checking_id"], data["payment_request"] - else: - error_message = r.json()["message"] return InvoiceResponse(ok, checking_id, payment_request, error_message) def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = post(url=f"{self.endpoint}/api/v1/payments", headers=self.auth_admin, json={"out": True, "bolt11": bolt11}) - ok, checking_id, fee_msat, error_message = True, None, 0, None + r = httpx.post(url=f"{self.endpoint}/api/v1/payments", headers=self.key, json={"out": True, "bolt11": bolt11}) + ok, checking_id, fee_msat, error_message = not r.is_error, None, 0, None - if r.ok: + if r.is_error: + error_message = r.json()["message"] + else: data = r.json() checking_id = data["checking_id"] - else: - error_message = r.json()["message"] return PaymentResponse(ok, checking_id, fee_msat, error_message) def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice) + r = httpx.get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key) - if not r.ok: + if r.is_error: return PaymentStatus(None) return PaymentStatus(r.json()["paid"]) def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice) + r = httpx.get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key) - if not r.ok: + if r.is_error: return PaymentStatus(None) return PaymentStatus(r.json()["paid"]) diff --git a/lnbits/wallets/lndgrpc.py b/lnbits/wallets/lndgrpc.py index 1a084b6e..5c25f99b 100644 --- a/lnbits/wallets/lndgrpc.py +++ b/lnbits/wallets/lndgrpc.py @@ -46,22 +46,28 @@ class LndWallet(Wallet): self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint self.port = int(getenv("LND_GRPC_PORT")) self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT") - self.auth_admin = getenv("LND_GRPC_ADMIN_MACAROON") or getenv("LND_ADMIN_MACAROON") - self.auth_invoices = getenv("LND_GRPC_INVOICE_MACAROON") or getenv("LND_INVOICE_MACAROON") + + self.macaroon_path = ( + getenv("LND_GRPC_MACAROON") + or getenv("LND_GRPC_ADMIN_MACAROON") + or getenv("LND_ADMIN_MACAROON") + or getenv("LND_GRPC_INVOICE_MACAROON") + or getenv("LND_INVOICE_MACAROON") + ) network = getenv("LND_GRPC_NETWORK", "mainnet") self.admin_rpc = lndgrpc.LNDClient( f"{self.endpoint}:{self.port}", cert_filepath=self.cert_path, network=network, - macaroon_filepath=self.auth_admin, + macaroon_filepath=self.macaroon_path, ) self.invoices_rpc = lndgrpc.LNDClient( f"{self.endpoint}:{self.port}", cert_filepath=self.cert_path, network=network, - macaroon_filepath=self.auth_invoices, + macaroon_filepath=self.macaroon_path, ) def create_invoice( @@ -129,7 +135,7 @@ class LndWallet(Wallet): ln.Invoice, ), ) - macaroon = load_macaroon(self.auth_admin) + macaroon = load_macaroon(self.macaroon_path) async for inv in subscribe_invoices( ln.InvoiceSubscription(), diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index 7ff8e8c2..309cfe3e 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -11,19 +11,20 @@ class LndRestWallet(Wallet): """https://api.lightning.community/rest/index.html#lnd-rest-api-reference""" def __init__(self): - endpoint = getenv("LND_REST_ENDPOINT") endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint endpoint = "https://" + endpoint if not endpoint.startswith("http") else endpoint self.endpoint = endpoint - self.auth_admin = { - "Grpc-Metadata-macaroon": getenv("LND_ADMIN_MACAROON") or getenv("LND_REST_ADMIN_MACAROON"), - } - self.auth_invoice = { - "Grpc-Metadata-macaroon": getenv("LND_INVOICE_MACAROON") or getenv("LND_REST_INVOICE_MACAROON") - } - self.auth_cert = getenv("LND_REST_CERT") + macaroon = ( + getenv("LND_MACAROON") + or getenv("LND_ADMIN_MACAROON") + or getenv("LND_REST_ADMIN_MACAROON") + or getenv("LND_INVOICE_MACAROON") + or getenv("LND_REST_INVOICE_MACAROON") + ) + self.auth = {"Grpc-Metadata-macaroon": macaroon} + self.cert = getenv("LND_REST_CERT") def create_invoice( self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None @@ -39,8 +40,8 @@ class LndRestWallet(Wallet): r = httpx.post( url=f"{self.endpoint}/v1/invoices", - headers=self.auth_invoice, - verify=self.auth_cert, + headers=self.auth, + verify=self.cert, json=data, ) @@ -62,8 +63,8 @@ class LndRestWallet(Wallet): def pay_invoice(self, bolt11: str) -> PaymentResponse: r = httpx.post( url=f"{self.endpoint}/v1/channels/transactions", - headers=self.auth_admin, - verify=self.auth_cert, + headers=self.auth, + verify=self.cert, json={"payment_request": bolt11}, ) @@ -84,8 +85,8 @@ class LndRestWallet(Wallet): checking_id = checking_id.replace("_", "/") r = httpx.get( url=f"{self.endpoint}/v1/invoice/{checking_id}", - headers=self.auth_invoice, - verify=self.auth_cert, + headers=self.auth, + verify=self.cert, ) if r.is_error or not r.json().get("settled"): @@ -98,8 +99,8 @@ class LndRestWallet(Wallet): def get_payment_status(self, checking_id: str) -> PaymentStatus: r = httpx.get( url=f"{self.endpoint}/v1/payments", - headers=self.auth_admin, - verify=self.auth_cert, + headers=self.auth, + verify=self.cert, params={"include_incomplete": "True", "max_payments": "20"}, ) @@ -118,7 +119,7 @@ class LndRestWallet(Wallet): async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: url = self.endpoint + "/v1/invoices/subscribe" - async with httpx.AsyncClient(timeout=None, headers=self.auth_admin, verify=self.auth_cert) as client: + async with httpx.AsyncClient(timeout=None, headers=self.auth, verify=self.cert) as client: async with client.stream("GET", url) as r: async for line in r.aiter_lines(): try: diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 782615b5..8543219c 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -15,8 +15,8 @@ class LNPayWallet(Wallet): def __init__(self): endpoint = getenv("LNPAY_API_ENDPOINT", "https://lnpay.co/v1") self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint - self.auth_admin = getenv("LNPAY_ADMIN_KEY") - self.auth_api = {"X-Api-Key": getenv("LNPAY_API_KEY")} + self.wallet_key = getenv("LNPAY_WALLET_KEY") or getenv("LNPAY_ADMIN_KEY") + self.auth = {"X-Api-Key": getenv("LNPAY_API_KEY")} def create_invoice( self, @@ -31,8 +31,8 @@ class LNPayWallet(Wallet): data["memo"] = memo or "" r = httpx.post( - url=f"{self.endpoint}/user/wallet/{self.auth_admin}/invoice", - headers=self.auth_api, + url=f"{self.endpoint}/user/wallet/{self.wallet_key}/invoice", + headers=self.auth, json=data, ) ok, checking_id, payment_request, error_message = ( @@ -50,8 +50,8 @@ class LNPayWallet(Wallet): def pay_invoice(self, bolt11: str) -> PaymentResponse: r = httpx.post( - url=f"{self.endpoint}/user/wallet/{self.auth_admin}/withdraw", - headers=self.auth_api, + url=f"{self.endpoint}/user/wallet/{self.wallet_key}/withdraw", + headers=self.auth, json={"payment_request": bolt11}, ) ok, checking_id, fee_msat, error_message = r.status_code == 201, None, 0, None @@ -67,7 +67,7 @@ class LNPayWallet(Wallet): def get_payment_status(self, checking_id: str) -> PaymentStatus: r = httpx.get( url=f"{self.endpoint}/user/lntx/{checking_id}?fields=settled", - headers=self.auth_api, + headers=self.auth, ) if r.is_error: @@ -91,7 +91,7 @@ class LNPayWallet(Wallet): async with httpx.AsyncClient() as client: r = await client.get( f"{self.endpoint}/user/lntx/{lntx_id}?fields=settled", - headers=self.auth_api, + headers=self.auth, ) data = r.json() if data["settled"]: diff --git a/lnbits/wallets/lntxbot.py b/lnbits/wallets/lntxbot.py index 1bbcd4d4..9ad7efa5 100644 --- a/lnbits/wallets/lntxbot.py +++ b/lnbits/wallets/lntxbot.py @@ -1,7 +1,7 @@ import trio # type: ignore +import httpx from os import getenv from typing import Optional, Dict, AsyncGenerator -from requests import post from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet @@ -12,8 +12,9 @@ class LntxbotWallet(Wallet): def __init__(self): endpoint = getenv("LNTXBOT_API_ENDPOINT") self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint - self.auth_admin = {"Authorization": f"Basic {getenv('LNTXBOT_ADMIN_KEY')}"} - self.auth_invoice = {"Authorization": f"Basic {getenv('LNTXBOT_INVOICE_KEY')}"} + + key = getenv("LNTXBOT_KEY") or getenv("LNTXBOT_ADMIN_KEY") or getenv("LNTXBOT_INVOICE_KEY") + self.auth = {"Authorization": f"Basic {key}"} def create_invoice( self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None @@ -24,44 +25,47 @@ class LntxbotWallet(Wallet): else: data["memo"] = memo or "" - r = post( + r = httpx.post( url=f"{self.endpoint}/addinvoice", - headers=self.auth_invoice, + headers=self.auth, json=data, ) - ok, checking_id, payment_request, error_message = r.ok, None, None, None - if r.ok: - data = r.json() - checking_id, payment_request = data["payment_hash"], data["pay_req"] - - if "error" in data and data["error"]: - ok = False + if r.is_error: + try: + data = r.json() error_message = data["message"] + except: + error_message = r.text + pass - return InvoiceResponse(ok, checking_id, payment_request, error_message) - - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = post(url=f"{self.endpoint}/payinvoice", headers=self.auth_admin, json={"invoice": bolt11}) - ok, checking_id, fee_msat, error_message = r.ok, None, 0, None - - if r.ok: - data = r.json() - - if "payment_hash" in data: - checking_id, fee_msat = data["decoded"]["payment_hash"], data["fee_msat"] - elif "error" in data and data["error"]: - ok, error_message = False, data["message"] - - return PaymentResponse(ok, checking_id, fee_msat, error_message) - - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = post(url=f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", headers=self.auth_invoice) - - if not r.ok or "error" in r.json(): - return PaymentStatus(None) + return InvoiceResponse(False, None, None, error_message) data = r.json() + return InvoiceResponse(True, data["payment_hash"], data["pay_req"], None) + + def pay_invoice(self, bolt11: str) -> PaymentResponse: + r = httpx.post(url=f"{self.endpoint}/payinvoice", headers=self.auth, json={"invoice": bolt11}) + + if r.is_error: + try: + data = r.json() + error_message = data["message"] + except: + error_message = r.text + pass + + return PaymentResponse(False, None, 0, error_message) + + data = r.json() + return PaymentResponse(True, data["decoded"]["payment_hash"], data["fee_msat"], None) + + def get_invoice_status(self, checking_id: str) -> PaymentStatus: + r = httpx.post(url=f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", headers=self.auth) + + data = r.json() + if r.is_error or "error" in data: + return PaymentStatus(None) if "preimage" not in data: return PaymentStatus(False) @@ -69,13 +73,14 @@ class LntxbotWallet(Wallet): return PaymentStatus(True) def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = post(url=f"{self.endpoint}/paymentstatus/{checking_id}", headers=self.auth_invoice) + r = httpx.post(url=f"{self.endpoint}/paymentstatus/{checking_id}", headers=self.auth) - if not r.ok or "error" in r.json(): + data = r.json() + if r.is_error or "error" in data: return PaymentStatus(None) statuses = {"complete": True, "failed": False, "pending": None, "unknown": None} - return PaymentStatus(statuses[r.json().get("status", "unknown")]) + return PaymentStatus(statuses[data.get("status", "unknown")]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: print("lntxbot does not support paid invoices stream yet") diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 497afd2c..3568f9eb 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -16,8 +16,9 @@ class OpenNodeWallet(Wallet): def __init__(self): endpoint = getenv("OPENNODE_API_ENDPOINT") self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint - self.auth_admin = {"Authorization": getenv("OPENNODE_ADMIN_KEY")} - self.auth_invoice = {"Authorization": getenv("OPENNODE_INVOICE_KEY")} + + key = getenv("OPENNODE_KEY") or getenv("OPENNODE_ADMIN_KEY") or getenv("OPENNODE_INVOICE_KEY") + self.auth = {"Authorization": key} def create_invoice( self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None @@ -45,9 +46,7 @@ class OpenNodeWallet(Wallet): return InvoiceResponse(True, checking_id, payment_request, None) def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - f"{self.endpoint}/v2/withdrawals", headers=self.auth_admin, json={"type": "ln", "address": bolt11} - ) + r = httpx.post(f"{self.endpoint}/v2/withdrawals", headers=self.auth, json={"type": "ln", "address": bolt11}) if r.is_error: error_message = r.json()["message"] @@ -68,7 +67,7 @@ class OpenNodeWallet(Wallet): return PaymentStatus(statuses[r.json()["data"]["status"]]) def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get(f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth_admin) + r = httpx.get(f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth) if r.is_error: return PaymentStatus(None) diff --git a/requirements.txt b/requirements.txt index 30136a08..711a624c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,16 +7,15 @@ blinker==1.4 brotli==1.0.9 cerberus==1.3.2 certifi==2020.6.20 -chardet==3.0.4 click==7.1.2 ecdsa==0.16.0 environs==8.0.0 -h11==0.9.0 +h11==0.11.0 h2==4.0.0 hpack==4.0.0 -httpcore==0.11.1 -httpx==0.15.5 -hypercorn==0.11.0 +httpcore==0.12.0 +httpx==0.16.1 +hypercorn==0.11.1 hyperframe==6.0.0 idna==2.10 itsdangerous==1.1.0 @@ -33,7 +32,6 @@ quart==0.13.1 quart-compress==0.2.1 quart-cors==0.3.0 quart-trio==0.5.1 -requests==2.24.0 rfc3986==1.4.0 secure==0.2.1 shortuuid==1.0.1 @@ -43,6 +41,5 @@ sortedcontainers==2.2.2 toml==0.10.1 trio==0.17.0 typing-extensions==3.7.4.3 -urllib3==1.25.10 werkzeug==1.0.1 wsproto==0.15.0