From 5f3f88c8ed585be363a56d8ce50c341238d5e360 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Thu, 18 May 2023 19:47:44 +0200 Subject: [PATCH] [Mint] rewrite lnbits backend with httpx (#230) * rewrite lnbits backend with httpx * add httpx --- cashu/lightning/lnbits.py | 99 ++++++++++++++++++++++----------------- poetry.lock | 68 ++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 122 insertions(+), 46 deletions(-) diff --git a/cashu/lightning/lnbits.py b/cashu/lightning/lnbits.py index e295543..eaa82b4 100644 --- a/cashu/lightning/lnbits.py +++ b/cashu/lightning/lnbits.py @@ -1,7 +1,7 @@ # type: ignore from typing import Dict, Optional -import requests +import httpx from ..core.settings import settings from .base import ( @@ -18,22 +18,23 @@ class LNbitsWallet(Wallet): def __init__(self): self.endpoint = settings.mint_lnbits_endpoint - key = settings.mint_lnbits_key - self.key = {"X-Api-Key": key} - self.s = requests.Session() - self.s.auth = ("user", "pass") - self.s.headers.update({"X-Api-Key": key}) - self.s.verify = not settings.debug + self.headers = {"X-Api-Key": key} + self.verify = not settings.debug async def status(self) -> StatusResponse: - try: - r = self.s.get(url=f"{self.endpoint}/api/v1/wallet", timeout=15) - r.raise_for_status() - except Exception as exc: - return StatusResponse( - f"Failed to connect to {self.endpoint} due to: {exc}", 0 - ) + async with httpx.AsyncClient(verify=self.verify) as client: + try: + r = await client.get( + url=f"{self.endpoint}/api/v1/wallet", + timeout=15, + headers=self.headers, + ) + r.raise_for_status() + except Exception as exc: + return StatusResponse( + f"Failed to connect to {self.endpoint} due to: {exc}", 0 + ) try: data = r.json() @@ -59,11 +60,16 @@ class LNbitsWallet(Wallet): data["unhashed_description"] = unhashed_description.hex() data["memo"] = memo or "" - try: - r = self.s.post(url=f"{self.endpoint}/api/v1/payments", json=data) - r.raise_for_status() - except: - return InvoiceResponse(False, None, None, r.json()["detail"]) + async with httpx.AsyncClient(verify=self.verify) as client: + try: + r = await client.post( + url=f"{self.endpoint}/api/v1/payments", + json=data, + headers=self.headers, + ) + r.raise_for_status() + except: + return InvoiceResponse(False, None, None, r.json()["detail"]) ok, checking_id, payment_request, error_message = ( True, None, @@ -77,16 +83,18 @@ class LNbitsWallet(Wallet): return InvoiceResponse(ok, checking_id, payment_request, error_message) async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: - try: - r = self.s.post( - url=f"{self.endpoint}/api/v1/payments", - json={"out": True, "bolt11": bolt11}, - timeout=None, - ) - r.raise_for_status() - except: - error_message = r.json()["detail"] - return PaymentResponse(None, None, None, None, error_message) + async with httpx.AsyncClient(verify=self.verify) as client: + try: + r = await client.post( + url=f"{self.endpoint}/api/v1/payments", + json={"out": True, "bolt11": bolt11}, + timeout=None, + headers=self.headers, + ) + r.raise_for_status() + except: + error_message = r.json()["detail"] + return PaymentResponse(None, None, None, None, error_message) if r.status_code > 299: return PaymentResponse(None, None, None, None, f"HTTP status: {r.reason}") if "detail" in r.json(): @@ -108,26 +116,29 @@ class LNbitsWallet(Wallet): return PaymentResponse(ok, checking_id, payment.fee_msat, payment.preimage) async def get_invoice_status(self, checking_id: str) -> PaymentStatus: - try: - r = self.s.get( - url=f"{self.endpoint}/api/v1/payments/{checking_id}", - headers=self.key, - ) - r.raise_for_status() - except: - return PaymentStatus(None) + async with httpx.AsyncClient(verify=self.verify) as client: + try: + r = await client.get( + url=f"{self.endpoint}/api/v1/payments/{checking_id}", + headers=self.headers, + ) + r.raise_for_status() + except: + return PaymentStatus(None) if r.json().get("detail"): return PaymentStatus(None) return PaymentStatus(r.json()["paid"]) async def get_payment_status(self, checking_id: str) -> PaymentStatus: - try: - r = self.s.get( - url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key - ) - r.raise_for_status() - except: - return PaymentStatus(None) + async with httpx.AsyncClient(verify=self.verify) as client: + try: + r = await client.get( + url=f"{self.endpoint}/api/v1/payments/{checking_id}", + headers=self.headers, + ) + r.raise_for_status() + except: + return PaymentStatus(None) data = r.json() if "paid" not in data and "details" not in data: return PaymentStatus(None) diff --git a/poetry.lock b/poetry.lock index 8aa1b70..b5e162c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "anyio" @@ -516,6 +516,52 @@ files = [ {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] +[[package]] +name = "httpcore" +version = "0.15.0" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"}, + {file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"}, +] + +[package.dependencies] +anyio = ">=3.0.0,<4.0.0" +certifi = "*" +h11 = ">=0.11,<0.13" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.23.0" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"}, + {file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.16.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + [[package]] name = "idna" version = "3.4" @@ -1084,6 +1130,24 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + [[package]] name = "secp256k1" version = "0.14.0" @@ -1422,4 +1486,4 @@ pgsql = ["psycopg2-binary"] [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "a59e7ba531891ae92d018a8d5947b71440caa3818d2d3392ef9873c6f95f0d77" +content-hash = "8ac516f63a6f6538a7cc1ed4b1db8a34b1fe1eaa174ae50b5979ce1ef42ec705" diff --git a/pyproject.toml b/pyproject.toml index 44bb8fe..f6c5e7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ setuptools = "^65.6.3" wheel = "^0.38.4" importlib-metadata = "^5.2.0" psycopg2-binary = {version = "^2.9.5", optional = true } +httpx = "0.23.0" [tool.poetry.extras] pgsql = ["psycopg2-binary"]