fix: switch from purerpc to grpcio for LND Wallet
purerpc is unmaintained and had some old dependencies.
This commit is contained in:
parent
54f4dfd838
commit
768aa05b11
5 changed files with 17710 additions and 49 deletions
|
|
@ -29,7 +29,7 @@ Using this wallet requires the installation of the `pylightning` Python package.
|
||||||
|
|
||||||
### LND (gRPC)
|
### LND (gRPC)
|
||||||
|
|
||||||
Using this wallet requires the installation of the `lndgrpc` and `purerpc` Python packages.
|
Using this wallet requires the installation of the `grpcio` Python packages.
|
||||||
|
|
||||||
- `LNBITS_BACKEND_WALLET_CLASS`: **LndWallet**
|
- `LNBITS_BACKEND_WALLET_CLASS`: **LndWallet**
|
||||||
- `LND_GRPC_ENDPOINT`: ip_address
|
- `LND_GRPC_ENDPOINT`: ip_address
|
||||||
|
|
|
||||||
0
lnbits/wallets/lnd_grpc_files/__init__.py
Normal file
0
lnbits/wallets/lnd_grpc_files/__init__.py
Normal file
14326
lnbits/wallets/lnd_grpc_files/lightning_pb2.py
Normal file
14326
lnbits/wallets/lnd_grpc_files/lightning_pb2.py
Normal file
File diff suppressed because one or more lines are too long
3340
lnbits/wallets/lnd_grpc_files/lightning_pb2_grpc.py
Normal file
3340
lnbits/wallets/lnd_grpc_files/lightning_pb2_grpc.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,17 @@
|
||||||
try:
|
try:
|
||||||
import lndgrpc # type: ignore
|
import grpc
|
||||||
from lndgrpc.common import ln # type: ignore
|
|
||||||
except ImportError: # pragma: nocover
|
except ImportError: # pragma: nocover
|
||||||
lndgrpc = None
|
grpc = None
|
||||||
|
|
||||||
try:
|
|
||||||
import purerpc # type: ignore
|
|
||||||
except ImportError: # pragma: nocover
|
|
||||||
purerpc = None
|
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from os import getenv
|
from os import environ, error, getenv
|
||||||
from typing import Optional, Dict, AsyncGenerator
|
from typing import Optional, Dict, AsyncGenerator
|
||||||
|
|
||||||
|
import lnbits.wallets.lnd_grpc_files.lightning_pb2 as ln
|
||||||
|
import lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc as lnrpc
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
|
|
@ -71,16 +68,17 @@ def stringify_checking_id(r_hash: bytes) -> str:
|
||||||
return base64.b64encode(r_hash).decode("utf-8").replace("/", "_")
|
return base64.b64encode(r_hash).decode("utf-8").replace("/", "_")
|
||||||
|
|
||||||
|
|
||||||
|
# Due to updated ECDSA generated tls.cert we need to let gprc know that
|
||||||
|
# we need to use that cipher suite otherwise there will be a handhsake
|
||||||
|
# error when we communicate with the lnd rpc server.
|
||||||
|
environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA"
|
||||||
|
|
||||||
|
|
||||||
class LndWallet(Wallet):
|
class LndWallet(Wallet):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if lndgrpc is None: # pragma: nocover
|
if grpc is None: # pragma: nocover
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"The `lndgrpc` library must be installed to use `LndWallet`."
|
"The `grpcio` library must be installed to use `GRPC LndWallet`. Alternatively try using the LndRESTWallet."
|
||||||
)
|
|
||||||
|
|
||||||
if purerpc is None: # pragma: nocover
|
|
||||||
raise ImportError(
|
|
||||||
"The `purerpc` library must be installed to use `LndWallet`."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
endpoint = getenv("LND_GRPC_ENDPOINT")
|
endpoint = getenv("LND_GRPC_ENDPOINT")
|
||||||
|
|
@ -88,25 +86,34 @@ class LndWallet(Wallet):
|
||||||
self.port = int(getenv("LND_GRPC_PORT"))
|
self.port = int(getenv("LND_GRPC_PORT"))
|
||||||
self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
|
self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
|
||||||
|
|
||||||
self.macaroon_path = (
|
macaroon_path = (
|
||||||
getenv("LND_GRPC_MACAROON")
|
getenv("LND_GRPC_MACAROON")
|
||||||
or getenv("LND_GRPC_ADMIN_MACAROON")
|
or getenv("LND_GRPC_ADMIN_MACAROON")
|
||||||
or getenv("LND_ADMIN_MACAROON")
|
or getenv("LND_ADMIN_MACAROON")
|
||||||
or getenv("LND_GRPC_INVOICE_MACAROON")
|
or getenv("LND_GRPC_INVOICE_MACAROON")
|
||||||
or getenv("LND_INVOICE_MACAROON")
|
or getenv("LND_INVOICE_MACAROON")
|
||||||
)
|
)
|
||||||
network = getenv("LND_GRPC_NETWORK", "mainnet")
|
|
||||||
|
|
||||||
self.rpc = lndgrpc.LNDClient(
|
if macaroon_path.split(".")[-1] == "macaroon":
|
||||||
f"{self.endpoint}:{self.port}",
|
self.macaroon = load_macaroon(macaroon_path)
|
||||||
cert_filepath=self.cert_path,
|
else:
|
||||||
network=network,
|
self.macaroon = macaroon_path
|
||||||
macaroon_filepath=self.macaroon_path,
|
|
||||||
|
cert = open(self.cert_path, "rb").read()
|
||||||
|
creds = grpc.ssl_channel_credentials(cert)
|
||||||
|
auth_creds = grpc.metadata_call_credentials(self.metadata_callback)
|
||||||
|
composite_creds = grpc.composite_channel_credentials(creds, auth_creds)
|
||||||
|
channel = grpc.aio.secure_channel(
|
||||||
|
f"{self.endpoint}:{self.port}", composite_creds
|
||||||
)
|
)
|
||||||
|
self.rpc = lnrpc.LightningStub(channel)
|
||||||
|
|
||||||
|
def metadata_callback(self, _, callback):
|
||||||
|
callback([("macaroon", self.macaroon)], None)
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
try:
|
try:
|
||||||
resp = self.rpc._ln_stub.ChannelBalance(ln.ChannelBalanceRequest())
|
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return StatusResponse(str(exc), 0)
|
return StatusResponse(str(exc), 0)
|
||||||
|
|
||||||
|
|
@ -127,7 +134,7 @@ class LndWallet(Wallet):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = ln.Invoice(**params)
|
req = ln.Invoice(**params)
|
||||||
resp = self.rpc._ln_stub.AddInvoice(req)
|
resp = await self.rpc.AddInvoice(req)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
error_message = str(exc)
|
error_message = str(exc)
|
||||||
return InvoiceResponse(False, None, None, error_message)
|
return InvoiceResponse(False, None, None, error_message)
|
||||||
|
|
@ -137,7 +144,9 @@ class LndWallet(Wallet):
|
||||||
return InvoiceResponse(True, checking_id, payment_request, None)
|
return InvoiceResponse(True, checking_id, payment_request, None)
|
||||||
|
|
||||||
async def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
async def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
||||||
resp = self.rpc.send_payment(payment_request=bolt11)
|
resp = await self.rpc.SendPayment(
|
||||||
|
lnrpc.SendPaymentRequest(payment_request=bolt11)
|
||||||
|
)
|
||||||
|
|
||||||
if resp.payment_error:
|
if resp.payment_error:
|
||||||
return PaymentResponse(False, "", 0, None, resp.payment_error)
|
return PaymentResponse(False, "", 0, None, resp.payment_error)
|
||||||
|
|
@ -158,7 +167,7 @@ class LndWallet(Wallet):
|
||||||
# that use different checking_id formats
|
# that use different checking_id formats
|
||||||
return PaymentStatus(None)
|
return PaymentStatus(None)
|
||||||
|
|
||||||
resp = self.rpc.lookup_invoice(r_hash.hex())
|
resp = await self.rpc.LookupInvoice(ln.PaymentHash(r_hash=r_hash))
|
||||||
if resp.settled:
|
if resp.settled:
|
||||||
return PaymentStatus(True)
|
return PaymentStatus(True)
|
||||||
|
|
||||||
|
|
@ -168,29 +177,15 @@ class LndWallet(Wallet):
|
||||||
return PaymentStatus(True)
|
return PaymentStatus(True)
|
||||||
|
|
||||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||||
async with purerpc.secure_channel(
|
request = ln.InvoiceSubscription()
|
||||||
self.endpoint, self.port, get_ssl_context(self.cert_path)
|
try:
|
||||||
) as channel:
|
async for i in self.rpc.SubscribeInvoices(request):
|
||||||
client = purerpc.Client("lnrpc.Lightning", channel)
|
if not i.settled:
|
||||||
subscribe_invoices = client.get_method_stub(
|
|
||||||
"SubscribeInvoices",
|
|
||||||
purerpc.RPCSignature(
|
|
||||||
purerpc.Cardinality.UNARY_STREAM, ln.InvoiceSubscription, ln.Invoice
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.macaroon_path.split(".")[-1] == "macaroon":
|
|
||||||
macaroon = load_macaroon(self.macaroon_path)
|
|
||||||
else:
|
|
||||||
macaroon = self.macaroon_path
|
|
||||||
|
|
||||||
async for inv in subscribe_invoices(
|
|
||||||
ln.InvoiceSubscription(), metadata=[("macaroon", macaroon)]
|
|
||||||
):
|
|
||||||
if not inv.settled:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
checking_id = stringify_checking_id(inv.r_hash)
|
checking_id = stringify_checking_id(i.r_hash)
|
||||||
yield checking_id
|
yield checking_id
|
||||||
|
except error:
|
||||||
|
print(error)
|
||||||
|
|
||||||
print("lost connection to lnd InvoiceSubscription, please restart lnbits.")
|
print("lost connection to lnd InvoiceSubscription, please restart lnbits.")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue