@@ -303,16 +303,47 @@ def create_uma_invoice(
303
303
amount_msats : int ,
304
304
metadata : str ,
305
305
expiry_secs : Optional [int ] = None ,
306
+ signing_private_key : Optional [bytes ] = None ,
307
+ receiver_identifier : Optional [str ] = None ,
306
308
) -> Invoice :
309
+ """Creates a new invoice for the UMA protocol. The metadata is hashed and included in the invoice. This API
310
+ generates a Lightning Invoice (follows the Bolt 11 specification) to request a payment from another Lightning Node.
311
+ This should only be used for generating invoices for UMA, with `create_invoice` preferred in the general case.
312
+
313
+ Args:
314
+ node_id: The node ID for which to create an invoice.
315
+ amount_msats: The amount of the invoice in msats. You can create a zero-amount invoice to accept any payment amount.
316
+ metadata: The LNURL metadata payload field in the initial payreq response. This wil be hashed and present in the
317
+ h-tag (SHA256 purpose of payment) of the resulting Bolt 11 invoice. See
318
+ [this spec](https://github.com/lnurl/luds/blob/luds/06.md#pay-to-static-qrnfclink) for details.
319
+ expiry_secs: The number of seconds until the invoice expires. Defaults to 600.
320
+ signing_private_key: The receiver's signing private key. Used to hash the receiver identifier.
321
+ receiver_identifier: Optional identifier of the receiver. If provided, this will be hashed using a monthly-rotated
322
+ seed and used for anonymized analysis.
323
+ """
324
+ receiver_hash = None
325
+ if receiver_identifier is not None :
326
+ if signing_private_key is None :
327
+ raise LightsparkException (
328
+ "CreateUmaInvoiceError" ,
329
+ "Receiver identifier provided without signing private key" ,
330
+ )
331
+ receiver_hash = self .hash_uma_identifier (
332
+ receiver_identifier , signing_private_key
333
+ )
334
+
335
+ variables = {
336
+ "amount_msats" : amount_msats ,
337
+ "node_id" : node_id ,
338
+ "metadata_hash" : sha256 (metadata .encode ("utf-8" )).hexdigest (),
339
+ "expiry_secs" : expiry_secs if expiry_secs is not None else 600 ,
340
+ }
341
+ if receiver_hash is not None :
342
+ variables ["receiver_hash" ] = receiver_hash
307
343
logger .info ("Creating an uma invoice for node %s." , node_id )
308
344
json = self ._requester .execute_graphql (
309
345
CREATE_UMA_INVOICE_MUTATION ,
310
- {
311
- "amount_msats" : amount_msats ,
312
- "node_id" : node_id ,
313
- "metadata_hash" : sha256 (metadata .encode ("utf-8" )).hexdigest (),
314
- "expiry_secs" : expiry_secs if expiry_secs is not None else 600 ,
315
- },
346
+ variables ,
316
347
)
317
348
318
349
return Invoice_from_json (self ._requester , json ["create_uma_invoice" ]["invoice" ])
@@ -530,7 +561,36 @@ def pay_uma_invoice(
530
561
maximum_fees_msats : int ,
531
562
amount_msats : Optional [int ] = None ,
532
563
idempotency_key : Optional [str ] = None ,
564
+ signing_private_key : Optional [bytes ] = None ,
565
+ sender_identifier : Optional [str ] = None ,
533
566
) -> OutgoingPayment :
567
+ """Sends an UMA payment to a node on the Lightning Network, based on the invoice (as defined by the BOLT11
568
+ specification) that you provide. This should only be used for paying UMA invoices, with `pay_invoice` preferred
569
+ in the general case.
570
+
571
+ Args:
572
+ node_id: The ID of the node that will pay the invoice.
573
+ encoded_invoice: The encoded invoice to pay.
574
+ timeout_secs: A timeout for the payment in seconds.
575
+ maximum_fees_msats: Maximum fees (in msats) to pay for the payment.
576
+ amount_msats: The amount to pay in msats for a zero-amount invoice. Defaults to the full amount of the
577
+ invoice. Note, this parameter can only be passed for a zero-amount invoice. Otherwise, the call will fail.
578
+ idempotency_key: An optional key to ensure idempotency of the payment.
579
+ signing_private_key: The sender's signing private key. Used to hash the sender identifier.
580
+ sender_identifier: Optional identifier of the sender. If provided, this will be hashed using a monthly-rotated
581
+ seed and used for anonymized analysis.
582
+ """
583
+ sender_hash = None
584
+ if sender_identifier is not None :
585
+ if signing_private_key is None :
586
+ raise LightsparkException (
587
+ "PayUmaInvoiceError" ,
588
+ "Sender identifier provided without signing private key" ,
589
+ )
590
+ sender_hash = self .hash_uma_identifier (
591
+ sender_identifier , signing_private_key
592
+ )
593
+
534
594
variables = {
535
595
"node_id" : node_id ,
536
596
"encoded_invoice" : encoded_invoice ,
@@ -541,6 +601,8 @@ def pay_uma_invoice(
541
601
variables ["amount_msats" ] = amount_msats
542
602
if idempotency_key is not None :
543
603
variables ["idempotency_key" ] = idempotency_key
604
+ if sender_hash is not None :
605
+ variables ["sender_hash" ] = sender_hash
544
606
json = self ._requester .execute_graphql (
545
607
PAY_UMA_INVOICE_MUTATION ,
546
608
variables ,
@@ -933,6 +995,11 @@ def _hash_phone_number(self, phone_number_e164_format: str) -> str:
933
995
)
934
996
return sha256 (phone_number_e164_format .encode ()).hexdigest ()
935
997
998
+ def hash_uma_identifier (self , identifier : str , signing_private_key : bytes ) -> str :
999
+ now = datetime .now (timezone .utc )
1000
+ input_data = identifier + f"{ now .month } -{ now .year } " + signing_private_key .hex ()
1001
+ return sha256 (input_data .encode ()).hexdigest ()
1002
+
936
1003
def fail_htlcs (self , invoice_id : str , cancel_invoice : bool = True ) -> str :
937
1004
"""
938
1005
Fails all pending HTLCs associated with an invoice.
0 commit comments