Mega-merge 3: CLN update client lib with descriptionhash support (WIP) (#792)

* CoreLightningWallet
This commit is contained in:
calle
2022-08-01 16:41:50 +02:00
committed by GitHub
parent f1ec7e33f0
commit 9c19b61e4a
6 changed files with 63 additions and 48 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
# flake8: noqa
from .cliche import ClicheWallet
from .clightning import CLightningWallet
from .cln import CoreLightningWallet # legacy .env support
from .cln import CoreLightningWallet as CLightningWallet
from .eclair import EclairWallet
from .fake import FakeWallet
from .lnbits import LNbitsWallet
@@ -1,5 +1,5 @@
try:
from lightning import LightningRpc, RpcError # type: ignore
from pyln.client import LightningRpc, RpcError # type: ignore
except ImportError: # pragma: nocover
LightningRpc = None
@@ -11,6 +11,8 @@ from functools import partial, wraps
from os import getenv
from typing import AsyncGenerator, Optional
from loguru import logger
from lnbits import bolt11 as lnbits_bolt11
from .base import (
@@ -42,26 +44,20 @@ def _paid_invoices_stream(ln, last_pay_index):
return ln.waitanyinvoice(last_pay_index)
class CLightningWallet(Wallet):
class CoreLightningWallet(Wallet):
def __init__(self):
if LightningRpc is None: # pragma: nocover
raise ImportError(
"The `pylightning` library must be installed to use `CLightningWallet`."
"The `pyln-client` library must be installed to use `CoreLightningWallet`."
)
self.rpc = getenv("CLIGHTNING_RPC")
self.rpc = getenv("CORELIGHTNING_RPC") or getenv("CLIGHTNING_RPC")
self.ln = LightningRpc(self.rpc)
# check description_hash support (could be provided by a plugin)
self.supports_description_hash = False
try:
answer = self.ln.help("invoicewithdescriptionhash")
if answer["help"][0]["command"].startswith(
"invoicewithdescriptionhash msatoshi label description_hash"
):
self.supports_description_hash = True
except:
pass
# check if description_hash is supported (from CLN>=v0.11.0)
self.supports_description_hash = (
"deschashonly" in self.ln.help("invoice")["help"][0]["command"]
)
# check last payindex so we can listen from that point on
self.last_pay_index = 0
@@ -89,21 +85,32 @@ class CLightningWallet(Wallet):
) -> InvoiceResponse:
label = "lbl{}".format(random.random())
msat = amount * 1000
try:
if description_hash:
if not self.supports_description_hash:
raise Unsupported("description_hash")
if description_hash and not self.supports_description_hash:
raise Unsupported("description_hash")
r = self.ln.invoice(
msatoshi=msat,
label=label,
description=description_hash.decode("utf-8")
if description_hash
else memo,
exposeprivatechannels=True,
deschashonly=True
if description_hash
else False, # we can't pass None here
)
params = [msat, label, hashlib.sha256(description_hash).hexdigest()]
r = self.ln.call("invoicewithdescriptionhash", params)
return InvoiceResponse(True, label, r["bolt11"], "")
else:
r = self.ln.invoice(msat, label, memo, exposeprivatechannels=True)
return InvoiceResponse(True, label, r["bolt11"], "")
if r.get("code") and r.get("code") < 0:
raise Exception(r.get("message"))
return InvoiceResponse(True, r["payment_hash"], r["bolt11"], "")
except RpcError as exc:
error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
return InvoiceResponse(False, label, None, error_message)
logger.error("RPC error:", error_message)
return InvoiceResponse(False, None, None, error_message)
except Exception as e:
logger.error("error:", e)
return InvoiceResponse(False, None, None, str(e))
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = lnbits_bolt11.decode(bolt11)
@@ -117,18 +124,19 @@ class CLightningWallet(Wallet):
try:
wrapped = async_wrap(_pay_invoice)
r = await wrapped(self.ln, payload)
except RpcError as exc:
except Exception as exc:
return PaymentResponse(False, None, 0, None, str(exc))
fee_msat = r["msatoshi_sent"] - r["msatoshi"]
preimage = r["payment_preimage"]
return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None)
return PaymentResponse(
True, r["payment_hash"], fee_msat, r["payment_preimage"], None
)
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = self.ln.listinvoices(checking_id)
r = self.ln.listinvoices(payment_hash=checking_id)
if not r["invoices"]:
return PaymentStatus(False)
if r["invoices"][0]["label"] == checking_id:
if r["invoices"][0]["payment_hash"] == checking_id:
return PaymentStatus(r["invoices"][0]["status"] == "paid")
raise KeyError("supplied an invalid checking_id")
@@ -147,7 +155,13 @@ class CLightningWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True:
wrapped = async_wrap(_paid_invoices_stream)
paid = await wrapped(self.ln, self.last_pay_index)
self.last_pay_index = paid["pay_index"]
yield paid["label"]
try:
wrapped = async_wrap(_paid_invoices_stream)
paid = await wrapped(self.ln, self.last_pay_index)
self.last_pay_index = paid["pay_index"]
yield paid["payment_hash"]
except Exception as exc:
logger.error(
f"lost connection to cln invoices stream: '{exc}', retrying in 5 seconds"
)
await asyncio.sleep(5)