Mega-merge 3: CLN update client lib with descriptionhash support (WIP) (#792)
* CoreLightningWallet
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user