From 0de08dda0baff6b01951184d527d6686e0738200 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sat, 29 Aug 2020 12:23:01 -0300 Subject: [PATCH] add spark backend and fix c-lightning. --- docs/guide/wallets.md | 5 +++ lnbits/wallets/__init__.py | 1 + lnbits/wallets/clightning.py | 24 ++++++---- lnbits/wallets/spark.py | 86 ++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 lnbits/wallets/spark.py diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md index 434a3ab..3eb5ea8 100644 --- a/docs/guide/wallets.md +++ b/docs/guide/wallets.md @@ -21,6 +21,11 @@ Using this wallet requires the installation of the `pylightning` Python package. - `LNBITS_BACKEND_WALLET_CLASS`: **CLightningWallet** - `CLIGHTNING_RPC`: /file/path/lightning-rpc +### Spark (c-lightning) + +- `LNBITS_BACKEND_WALLET_CLASS`: **SparkWallet** +- `SPARK_URL`: http://10.147.17.230:9737/rpc +- `SPARK_TOKEN`: secret_access_key ### LND (gRPC) diff --git a/lnbits/wallets/__init__.py b/lnbits/wallets/__init__.py index bfe8f21..263f5e0 100644 --- a/lnbits/wallets/__init__.py +++ b/lnbits/wallets/__init__.py @@ -7,3 +7,4 @@ from .opennode import OpenNodeWallet from .lnpay import LNPayWallet from .lnbits import LNbitsWallet from .lndrest import LndRestWallet +from .spark import SparkWallet diff --git a/lnbits/wallets/clightning.py b/lnbits/wallets/clightning.py index 051a4ff..a0d750b 100644 --- a/lnbits/wallets/clightning.py +++ b/lnbits/wallets/clightning.py @@ -28,20 +28,26 @@ class CLightningWallet(Wallet): def pay_invoice(self, bolt11: str) -> PaymentResponse: r = self.l1.pay(bolt11) - ok, checking_id, fee_msat, error_message = True, None, 0, None + ok, checking_id, fee_msat, error_message = True, r["payment_hash"], r["msatoshi_sent"] - r["msatoshi"], None return PaymentResponse(ok, checking_id, fee_msat, error_message) def get_invoice_status(self, checking_id: str) -> PaymentStatus: r = self.l1.listinvoices(checking_id) - if r["invoices"][0]["status"] == "unpaid": + if not r["invoices"]: return PaymentStatus(False) - return PaymentStatus(True) + if r["invoices"][0]["label"] == checking_id: + return PaymentStatus(r["pays"][0]["status"] == "paid") + raise KeyError("supplied an invalid checking_id") def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = self.l1.listsendpays(checking_id) - if not r.ok: + r = self.l1.listpays(payment_hash=checking_id) + if not r["pays"]: + return PaymentStatus(False) + if r["pays"][0]["payment_hash"] == checking_id: + status = r["pays"][0]["status"] + if status == "complete": + return PaymentStatus(True) + elif status == "failed": + return PaymentStatus(False) return PaymentStatus(None) - payments = [p for p in r.json()["payments"] if p["payment_hash"] == checking_id] - payment = payments[0] if payments else None - statuses = {"UNKNOWN": None, "IN_FLIGHT": None, "SUCCEEDED": True, "FAILED": False} - return PaymentStatus(statuses[payment["status"]] if payment else None) + raise KeyError("supplied an invalid checking_id") diff --git a/lnbits/wallets/spark.py b/lnbits/wallets/spark.py new file mode 100644 index 0000000..03e48aa --- /dev/null +++ b/lnbits/wallets/spark.py @@ -0,0 +1,86 @@ +import random +import requests +from os import getenv + +from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet + + +class SparkError(Exception): + pass + + +class UnknownError(Exception): + pass + + +class SparkWallet(Wallet): + def __init__(self): + self.url = getenv("SPARK_URL") + self.token = getenv("SPARK_TOKEN") + + def __getattr__(self, key): + def call(*args, **kwargs): + if args and kwargs: + raise TypeError(f"must supply either named arguments or a list of arguments, not both: {args} {kwargs}") + elif args: + params = args + elif kwargs: + params = kwargs + + r = requests.post(self.url, headers={"X-Access": self.token}, json={"method": key, "params": params}) + try: + data = r.json() + except: + raise UnknownError(r.text) + if not r.ok: + raise SparkError(data["message"]) + return data + + return call + + def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse: + label = "lbs{}".format(random.random()) + print(description_hash, len(description_hash)) + + try: + if description_hash: + r = self.invoicewithdescriptionhash( + msatoshi=amount * 1000, label=label, description_hash=description_hash.hex(), + ) + else: + r = self.invoice(msatoshi=amount * 1000, label=label, description=memo, exposeprivatechannels=True) + ok, checking_id, payment_request, error_message = True, label, r["bolt11"], None + except (SparkError, UnknownError) as e: + ok, checking_id, payment_request, error_message = False, None, None, str(e) + + return InvoiceResponse(ok, checking_id, payment_request, error_message) + + def pay_invoice(self, bolt11: str) -> PaymentResponse: + try: + r = self.pay(bolt11) + ok, checking_id, fee_msat, error_message = True, r["payment_hash"], r["msatoshi_sent"] - r["msatoshi"], None + except (SparkError, UnknownError) as e: + ok, checking_id, fee_msat, error_message = False, None, None, str(e) + + return PaymentResponse(ok, checking_id, fee_msat, error_message) + + def get_invoice_status(self, checking_id: str) -> PaymentStatus: + r = self.listinvoices(label=checking_id) + if not r or not r.get("invoices"): + return PaymentStatus(None) + if r["invoices"][0]["status"] == "unpaid": + return PaymentStatus(False) + return PaymentStatus(True) + + def get_payment_status(self, checking_id: str) -> PaymentStatus: + r = self.listpays(payment_hash=checking_id) + if not r["pays"]: + return PaymentStatus(False) + if r["pays"][0]["payment_hash"] == checking_id: + status = r["pays"][0]["status"] + if status == "complete": + return PaymentStatus(True) + elif status == "failed": + return PaymentStatus(False) + return PaymentStatus(None) + raise KeyError("supplied an invalid checking_id")