2020-05-02 19:00:01 +02:00
|
|
|
try:
|
2020-10-02 22:13:33 +02:00
|
|
|
from lightning import LightningRpc, RpcError # type: ignore
|
2020-05-02 19:00:01 +02:00
|
|
|
except ImportError: # pragma: nocover
|
|
|
|
LightningRpc = None
|
|
|
|
|
|
|
|
import random
|
|
|
|
|
2020-04-03 12:27:36 +02:00
|
|
|
from os import getenv
|
2020-10-02 22:13:33 +02:00
|
|
|
from typing import Optional, AsyncGenerator
|
2020-06-08 00:46:16 +02:00
|
|
|
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
|
2020-05-02 19:00:01 +02:00
|
|
|
|
2020-04-03 12:21:10 +02:00
|
|
|
|
|
|
|
class CLightningWallet(Wallet):
|
|
|
|
def __init__(self):
|
2020-05-02 19:00:01 +02:00
|
|
|
if LightningRpc is None: # pragma: nocover
|
|
|
|
raise ImportError("The `pylightning` library must be installed to use `CLightningWallet`.")
|
|
|
|
|
2020-10-02 22:13:33 +02:00
|
|
|
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
|
|
|
|
2020-08-31 04:48:46 +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())
|
2020-10-02 22:13:33 +02:00
|
|
|
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:
|
2020-10-02 22:13:33 +02:00
|
|
|
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:
|
2020-10-02 22:13:33 +02:00
|
|
|
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:
|
2020-10-02 22:13:33 +02:00
|
|
|
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")
|
2020-10-02 22:13:33 +02:00
|
|
|
|
|
|
|
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"]
|