Merge pull request #400 from arcbtc/FastAPI

fix: switch from purerpc to grpcio for LND Wallet
This commit is contained in:
Arc 2021-11-07 18:48:22 +00:00 committed by GitHub
commit 5eae8e1b11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 17710 additions and 49 deletions

View File

@ -29,7 +29,7 @@ Using this wallet requires the installation of the `pylightning` Python package.
### 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**
- `LND_GRPC_ENDPOINT`: ip_address

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,17 @@
try:
import lndgrpc # type: ignore
from lndgrpc.common import ln # type: ignore
import grpc
except ImportError: # pragma: nocover
lndgrpc = None
try:
import purerpc # type: ignore
except ImportError: # pragma: nocover
purerpc = None
grpc = None
import binascii
import base64
import hashlib
from os import getenv
from os import environ, error, getenv
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 (
StatusResponse,
InvoiceResponse,
@ -71,16 +68,17 @@ def stringify_checking_id(r_hash: bytes) -> str:
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):
def __init__(self):
if lndgrpc is None: # pragma: nocover
if grpc is None: # pragma: nocover
raise ImportError(
"The `lndgrpc` library must be installed to use `LndWallet`."
)
if purerpc is None: # pragma: nocover
raise ImportError(
"The `purerpc` library must be installed to use `LndWallet`."
"The `grpcio` library must be installed to use `GRPC LndWallet`. Alternatively try using the LndRESTWallet."
)
endpoint = getenv("LND_GRPC_ENDPOINT")
@ -88,25 +86,34 @@ class LndWallet(Wallet):
self.port = int(getenv("LND_GRPC_PORT"))
self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
self.macaroon_path = (
macaroon_path = (
getenv("LND_GRPC_MACAROON")
or getenv("LND_GRPC_ADMIN_MACAROON")
or getenv("LND_ADMIN_MACAROON")
or getenv("LND_GRPC_INVOICE_MACAROON")
or getenv("LND_INVOICE_MACAROON")
)
network = getenv("LND_GRPC_NETWORK", "mainnet")
self.rpc = lndgrpc.LNDClient(
f"{self.endpoint}:{self.port}",
cert_filepath=self.cert_path,
network=network,
macaroon_filepath=self.macaroon_path,
if macaroon_path.split(".")[-1] == "macaroon":
self.macaroon = load_macaroon(macaroon_path)
else:
self.macaroon = 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:
try:
resp = self.rpc._ln_stub.ChannelBalance(ln.ChannelBalanceRequest())
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
except Exception as exc:
return StatusResponse(str(exc), 0)
@ -127,7 +134,7 @@ class LndWallet(Wallet):
try:
req = ln.Invoice(**params)
resp = self.rpc._ln_stub.AddInvoice(req)
resp = await self.rpc.AddInvoice(req)
except Exception as exc:
error_message = str(exc)
return InvoiceResponse(False, None, None, error_message)
@ -137,7 +144,9 @@ class LndWallet(Wallet):
return InvoiceResponse(True, checking_id, payment_request, None)
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:
return PaymentResponse(False, "", 0, None, resp.payment_error)
@ -158,7 +167,7 @@ class LndWallet(Wallet):
# that use different checking_id formats
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:
return PaymentStatus(True)
@ -168,29 +177,15 @@ class LndWallet(Wallet):
return PaymentStatus(True)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
async with purerpc.secure_channel(
self.endpoint, self.port, get_ssl_context(self.cert_path)
) as channel:
client = purerpc.Client("lnrpc.Lightning", channel)
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:
request = ln.InvoiceSubscription()
try:
async for i in self.rpc.SubscribeInvoices(request):
if not i.settled:
continue
checking_id = stringify_checking_id(inv.r_hash)
checking_id = stringify_checking_id(i.r_hash)
yield checking_id
except error:
print(error)
print("lost connection to lnd InvoiceSubscription, please restart lnbits.")