lnbits-legend/lnbits/wallets/clightning.py

88 lines
3.5 KiB
Python
Raw Normal View History

try:
from lightning import LightningRpc, RpcError # type: ignore
except ImportError: # pragma: nocover
LightningRpc = None
import random
2020-04-03 12:27:36 +02:00
from os import getenv
from typing import Optional, AsyncGenerator
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
2020-04-03 12:21:10 +02:00
class CLightningWallet(Wallet):
def __init__(self):
if LightningRpc is None: # pragma: nocover
raise ImportError("The `pylightning` library must be installed to use `CLightningWallet`.")
self.ln = LightningRpc(getenv("CLIGHTNING_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 last payindex so we can listen from that point on
self.last_pay_index = 0
invoices = self.ln.listinvoices()
if len(invoices["invoices"]):
self.last_pay_index = invoices["invoices"][-1]["pay_index"]
2020-04-26 13:28:19 +02:00
def create_invoice(
self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None
) -> InvoiceResponse:
2020-04-26 13:28:19 +02:00
label = "lbl{}".format(random.random())
msat = amount * 1000
try:
if description_hash:
if not self.supports_description_hash:
raise Unsupported("description_hash")
r = self.ln.call("invoicewithdescriptionhash", [msat, label, memo])
return InvoiceResponse(True, label, r["bolt11"], "")
else:
r = self.ln.invoice(msat, label, memo, exposeprivatechannels=True)
return InvoiceResponse(True, label, r["bolt11"], "")
except RpcError as exc:
error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
return InvoiceResponse(False, label, None, error_message)
2020-04-03 12:21:10 +02:00
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = self.ln.pay(bolt11)
2020-08-29 17:23:01 +02:00
ok, checking_id, fee_msat, error_message = True, r["payment_hash"], r["msatoshi_sent"] - r["msatoshi"], None
2020-04-03 12:21:10 +02:00
return PaymentResponse(ok, checking_id, fee_msat, error_message)
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = self.ln.listinvoices(checking_id)
2020-08-29 17:23:01 +02:00
if not r["invoices"]:
2020-04-03 12:21:10 +02:00
return PaymentStatus(False)
2020-08-29 17:23:01 +02:00
if r["invoices"][0]["label"] == checking_id:
return PaymentStatus(r["pays"][0]["status"] == "paid")
raise KeyError("supplied an invalid checking_id")
2020-04-03 12:21:10 +02:00
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = self.ln.listpays(payment_hash=checking_id)
2020-08-29 17:23:01 +02:00
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)
2020-04-26 13:28:19 +02:00
return PaymentStatus(None)
2020-08-29 17:23:01 +02:00
raise KeyError("supplied an invalid checking_id")
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True:
paid = self.ln.waitanyinvoice(self.last_pay_index)
self.last_pay_index = paid["pay_index"]
yield paid["label"]