From a569a71272eeb4f331aa55756f4c3b53a0f60369 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:34:04 +0200 Subject: [PATCH] fix for lndgrpc --- cashu/lightning/lnd_grpc/lnd_grpc.py | 94 ++++++++++++++++------------ mypy.ini | 3 + 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/cashu/lightning/lnd_grpc/lnd_grpc.py b/cashu/lightning/lnd_grpc/lnd_grpc.py index 0c38e623..fa667ee0 100644 --- a/cashu/lightning/lnd_grpc/lnd_grpc.py +++ b/cashu/lightning/lnd_grpc/lnd_grpc.py @@ -45,39 +45,43 @@ lnrpc.Invoice.InvoiceState.ACCEPTED: None, } -class LndRPCWallet(LightningBackend): +class LndRPCWallet(LightningBackend): supports_mpp = settings.mint_lnd_enable_mpp supports_incoming_payment_stream = True supported_units = set([Unit.sat, Unit.msat]) + supports_description: bool = True + unit = Unit.sat def __init__(self, unit: Unit = Unit.sat, **kwargs): self.assert_unit_supported(unit) self.unit = unit - self.endpoint = settings.mint_lnd_rpc_endpoint cert_path = settings.mint_lnd_rpc_cert macaroon_path = settings.mint_lnd_rpc_macaroon - if not self.endpoint: + if not settings.mint_lnd_rpc_endpoint: raise Exception("cannot initialize LndRPCWallet: no endpoint") + self.endpoint = settings.mint_lnd_rpc_endpoint + if not macaroon_path: raise Exception("cannot initialize LndRPCWallet: no macaroon") if not cert_path: raise Exception("no certificate for LndRPCWallet provided") - self.macaroon = codecs.encode(open(macaroon_path, 'rb').read(), 'hex') + self.macaroon = codecs.encode(open(macaroon_path, "rb").read(), "hex") def metadata_callback(context, callback): - callback([('macaroon', self.macaroon)], None) + callback([("macaroon", self.macaroon)], None) + auth_creds = grpc.metadata_call_credentials(metadata_callback) # create SSL credentials - os.environ['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA' - cert = open(cert_path, 'rb').read() + os.environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA" + cert = open(cert_path, "rb").read() ssl_creds = grpc.ssl_channel_credentials(cert) # combine macaroon and SSL credentials @@ -86,11 +90,12 @@ def metadata_callback(context, callback): if self.supports_mpp: logger.info("LndRPCWallet enabling MPP feature") - async def status(self) -> StatusResponse: r = None try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) r = await lnstub.ChannelBalance(lnrpc.ChannelBalanceRequest()) except AioRpcError as e: @@ -98,9 +103,8 @@ async def status(self) -> StatusResponse: error_message=f"Error calling Lnd gRPC: {e}", balance=0 ) # NOTE: `balance` field is deprecated. Change this. - return StatusResponse(error_message=None, balance=r.balance*1000) + return StatusResponse(error_message=None, balance=r.balance * 1000) - async def create_invoice( self, amount: Amount, @@ -124,7 +128,9 @@ async def create_invoice( r = None try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) r = await lnstub.AddInvoice(data) except AioRpcError as e: @@ -144,7 +150,7 @@ async def create_invoice( payment_request=payment_request, error_message=None, ) - + async def pay_invoice( self, quote: MeltQuote, fee_limit_msat: int ) -> PaymentResponse: @@ -159,12 +165,12 @@ async def pay_invoice( ) # set the fee limit for the payment - feelimit = lnrpc.FeeLimit( - fixed_msat=fee_limit_msat - ) + feelimit = lnrpc.FeeLimit(fixed_msat=fee_limit_msat) r = None try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) r = await lnstub.SendPaymentSync( lnrpc.SendRequest( @@ -195,14 +201,12 @@ async def pay_invoice( preimage=preimage, error_message=None, ) - + async def pay_partial_invoice( self, quote: MeltQuote, amount: Amount, fee_limit_msat: int ) -> PaymentResponse: # set the fee limit for the payment - feelimit = lnrpc.FeeLimit( - fixed_msat=fee_limit_msat - ) + feelimit = lnrpc.FeeLimit(fixed_msat=fee_limit_msat) invoice = bolt11.decode(quote.request) invoice_amount = invoice.amount_msat @@ -220,7 +224,9 @@ async def pay_partial_invoice( # get the route r = None try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) router_stub = routerstub.RouterStub(channel) r = await lnstub.QueryRoutes( @@ -230,23 +236,27 @@ async def pay_partial_invoice( fee_limit=feelimit, ) ) - ''' + """ # We need to set the mpp_record for a partial payment mpp_record = lnrpc.MPPRecord( payment_addr=bytes.fromhex(payer_addr), total_amt_msat=total_amount_msat, ) - ''' + """ # modify the mpp_record in the last hop route_nr = 0 - r.routes[route_nr].hops[-1].mpp_record.payment_addr = bytes.fromhex(payer_addr) - r.routes[route_nr].hops[-1].mpp_record.total_amt_msat = total_amount_msat + r.routes[route_nr].hops[-1].mpp_record.payment_addr = bytes.fromhex( # type: ignore + payer_addr + ) + r.routes[route_nr].hops[ # type: ignore + -1 + ].mpp_record.total_amt_msat = total_amount_msat # Send to route request r = await router_stub.SendToRouteV2( routerrpc.SendToRouteRequest( payment_hash=bytes.fromhex(invoice.payment_hash), - route=r.routes[route_nr], + route=r.routes[route_nr], # type: ignore ) ) except AioRpcError as e: @@ -275,16 +285,16 @@ async def pay_partial_invoice( preimage=preimage, error_message=None, ) - + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: r = None try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) r = await lnstub.LookupInvoice( - lnrpc.PaymentHash( - r_hash=bytes.fromhex(checking_id) - ) + lnrpc.PaymentHash(r_hash=bytes.fromhex(checking_id)) ) except AioRpcError as e: error_message = f"LookupInvoice failed: {e}" @@ -292,7 +302,7 @@ async def get_invoice_status(self, checking_id: str) -> PaymentStatus: return PaymentStatus(paid=None) return PaymentStatus(paid=INVOICE_STATUSES[r.state]) - + async def get_payment_status(self, checking_id: str) -> PaymentStatus: """ This routine checks the payment status using routerpc.TrackPaymentV2. @@ -307,7 +317,9 @@ async def get_payment_status(self, checking_id: str) -> PaymentStatus: request = routerrpc.TrackPaymentRequest(payment_hash=checking_id_bytes) try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: router_stub = routerstub.RouterStub(channel) async for payment in router_stub.TrackPaymentV2(request): if payment is not None and payment.status: @@ -325,21 +337,23 @@ async def get_payment_status(self, checking_id: str) -> PaymentStatus: logger.error(error_message) return PaymentStatus(paid=None) - + async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: try: - async with grpc.aio.secure_channel(self.endpoint, self.combined_creds) as channel: + async with grpc.aio.secure_channel( + self.endpoint, self.combined_creds + ) as channel: lnstub = lightningstub.LightningStub(channel) - async for invoice in lnstub.SubscribeInvoices(lnrpc.InvoiceSubscription()): + async for invoice in lnstub.SubscribeInvoices( + lnrpc.InvoiceSubscription() + ): if invoice.state != lnrpc.Invoice.InvoiceState.SETTLED: continue payment_hash = invoice.r_hash.hex() yield payment_hash except AioRpcError as exc: - logger.error( - f"SubscribeInvoices failed: {exc}. Retrying in 1 sec..." - ) + logger.error(f"SubscribeInvoices failed: {exc}. Retrying in 1 sec...") await asyncio.sleep(1) async def get_payment_quote( diff --git a/mypy.ini b/mypy.ini index 0df56bd0..087ac3c0 100644 --- a/mypy.ini +++ b/mypy.ini @@ -6,3 +6,6 @@ ignore_missing_imports = True [mypy-cashu.nostr.*] ignore_errors = True + +[mypy-cashu.lightning.lnd_grpc.protos.*] +ignore_errors = True