feat: preimages for incoming payments, fundingsource saves preimage on create_invoice (#3085)

Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
dni ⚡
2025-04-30 11:03:19 +02:00
committed by GitHub
co-authored by Vlad Stan
parent f8b3644029
commit c4d0540e76
20 changed files with 183 additions and 106 deletions
+34 -19
View File
@@ -1,6 +1,6 @@
import asyncio
import base64
import hashlib
from hashlib import sha256
from os import environ
from typing import AsyncGenerator, Dict, Optional
@@ -12,7 +12,7 @@ import lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc as lnrpc
import lnbits.wallets.lnd_grpc_files.router_pb2 as router
import lnbits.wallets.lnd_grpc_files.router_pb2_grpc as routerrpc
from lnbits.settings import settings
from lnbits.utils.crypto import AESCipher
from lnbits.utils.crypto import AESCipher, random_secret_and_hash
from .base import (
InvoiceResponse,
@@ -139,22 +139,37 @@ class LndWallet(Wallet):
if description_hash:
data["description_hash"] = description_hash
elif unhashed_description:
data["description_hash"] = hashlib.sha256(
unhashed_description
).digest() # as bytes directly
data["description_hash"] = sha256(unhashed_description).digest()
preimage = kwargs.get("preimage")
if preimage:
payment_hash = sha256(preimage.encode()).hexdigest()
else:
preimage, payment_hash = random_secret_and_hash()
data["r_hash"] = bytes.fromhex(payment_hash)
data["r_preimage"] = bytes.fromhex(preimage)
try:
req = ln.Invoice(**data)
resp = await self.rpc.AddInvoice(req)
# response model
# {
# "r_hash": <bytes>,
# "payment_request": <string>,
# "add_index": <uint64>,
# "payment_addr": <bytes>,
# }
except Exception as exc:
logger.warning(exc)
error_message = str(exc)
return InvoiceResponse(ok=False, error_message=error_message)
return InvoiceResponse(ok=False, error_message=str(exc))
checking_id = bytes_to_hex(resp.r_hash)
payment_request = str(resp.payment_request)
return InvoiceResponse(
ok=True, checking_id=checking_id, payment_request=payment_request
ok=True,
checking_id=checking_id,
payment_request=payment_request,
preimage=preimage,
)
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
@@ -197,16 +212,18 @@ class LndWallet(Wallet):
fee_msat = -resp.htlcs[-1].route.total_fees_msat
preimage = resp.payment_preimage
checking_id = resp.payment_hash
return PaymentResponse(
ok=True, checking_id=checking_id, fee_msat=fee_msat, preimage=preimage
)
elif statuses[resp.status] is False:
error_message = failure_reasons[resp.failure_reason]
return PaymentResponse(
ok=statuses[resp.status],
checking_id=checking_id,
fee_msat=fee_msat,
preimage=preimage,
error_message=error_message,
)
return PaymentResponse(ok=False, error_message=error_message)
else:
return PaymentResponse(
ok=None,
checking_id=checking_id,
error_message="Payment in flight or non-existant.",
)
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
try:
@@ -217,10 +234,8 @@ class LndWallet(Wallet):
raise ValueError
resp = await self.rpc.LookupInvoice(ln.PaymentHash(r_hash=r_hash))
# todo: where is the FAILED status
if resp.settled:
return PaymentSuccessStatus()
return PaymentSuccessStatus(preimage=resp.r_preimage.hex())
return PaymentPendingStatus()
except grpc.RpcError as exc: