Mega-merge 3: CLN update client lib with descriptionhash support (WIP) (#792)
* CoreLightningWallet
This commit is contained in:
parent
f1ec7e33f0
commit
9c19b61e4a
|
@ -35,7 +35,7 @@ LNBITS_THEME_OPTIONS="classic, bitcoin, freedom, mint, autumn, monochrome, salva
|
|||
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
|
||||
|
||||
# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet
|
||||
# LndRestWallet, CLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet
|
||||
# LndRestWallet, CoreLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet
|
||||
LNBITS_BACKEND_WALLET_CLASS=VoidWallet
|
||||
# VoidWallet is just a fallback that works without any actual Lightning capabilities,
|
||||
# just so you can see the UI before dealing with this file.
|
||||
|
@ -49,8 +49,8 @@ CLICHE_ENDPOINT=ws://127.0.0.1:12000
|
|||
SPARK_URL=http://localhost:9737/rpc
|
||||
SPARK_TOKEN=myaccesstoken
|
||||
|
||||
# CLightningWallet
|
||||
CLIGHTNING_RPC="/home/bob/.lightning/bitcoin/lightning-rpc"
|
||||
# CoreLightningWallet
|
||||
CORELIGHTNING_RPC="/home/bob/.lightning/bitcoin/lightning-rpc"
|
||||
|
||||
# LnbitsWallet
|
||||
LNBITS_ENDPOINT=https://legend.lnbits.com
|
||||
|
|
10
.github/workflows/regtest.yml
vendored
10
.github/workflows/regtest.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
|||
python -m venv ${{ env.VIRTUAL_ENV }}
|
||||
./venv/bin/python -m pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/pip install pylightning
|
||||
./venv/bin/pip install pyln-client
|
||||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run tests
|
||||
env:
|
||||
|
@ -47,7 +47,7 @@ jobs:
|
|||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
CLightningWallet:
|
||||
CoreLightningWallet:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
@ -73,15 +73,15 @@ jobs:
|
|||
python -m venv ${{ env.VIRTUAL_ENV }}
|
||||
./venv/bin/python -m pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/pip install pylightning
|
||||
./venv/bin/pip install pyln-client
|
||||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run tests
|
||||
env:
|
||||
PYTHONUNBUFFERED: 1
|
||||
PORT: 5123
|
||||
LNBITS_DATA_FOLDER: ./data
|
||||
LNBITS_BACKEND_WALLET_CLASS: CLightningWallet
|
||||
CLIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc
|
||||
LNBITS_BACKEND_WALLET_CLASS: CoreLightningWallet
|
||||
CORELIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc
|
||||
run: |
|
||||
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
|
||||
make test-real-wallet
|
||||
|
|
|
@ -17,7 +17,7 @@ COPY requirements.txt /tmp/requirements.txt
|
|||
RUN pip install -r /tmp/requirements.txt
|
||||
|
||||
# Install c-lightning specific deps
|
||||
RUN pip install pylightning
|
||||
RUN pip install pyln-client
|
||||
|
||||
# Install LND specific deps
|
||||
RUN pip install lndgrpc
|
||||
|
|
|
@ -9,17 +9,17 @@ Backend wallets
|
|||
===============
|
||||
|
||||
LNbits can run on top of many lightning-network funding sources. Currently there is support for
|
||||
CLightning, LND, LNbits, LNPay, lntxbot and OpenNode, with more being added regularily.
|
||||
CoreLightning, LND, LNbits, LNPay, lntxbot and OpenNode, with more being added regularily.
|
||||
|
||||
A backend wallet can be configured using the following LNbits environment variables:
|
||||
|
||||
|
||||
### CLightning
|
||||
### CoreLightning
|
||||
|
||||
Using this wallet requires the installation of the `pylightning` Python package.
|
||||
|
||||
- `LNBITS_BACKEND_WALLET_CLASS`: **CLightningWallet**
|
||||
- `CLIGHTNING_RPC`: /file/path/lightning-rpc
|
||||
- `LNBITS_BACKEND_WALLET_CLASS`: **CoreLightningWallet**
|
||||
- `CORELIGHTNING_RPC`: /file/path/lightning-rpc
|
||||
|
||||
### Spark (c-lightning)
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue
Block a user