@@ -85,6 +85,7 @@ class PaymentIdentifierType(IntEnum):
8585 OPENALIAS = 8
8686 LNADDR = 9
8787 DOMAINLIKE = 10
88+ SILENT_PAYMENT = 11
8889
8990
9091class FieldsForGUI (NamedTuple ):
@@ -138,6 +139,8 @@ def __init__(self, wallet: Optional['Abstract_Wallet'], text: str):
138139 #
139140 self .lnurl = None
140141 self .lnurl_data = None
142+ #
143+ self .sp_address = None # used when a single silent payment is identified
141144
142145 self .parse (text )
143146
@@ -173,7 +176,7 @@ def is_lightning(self):
173176
174177 def is_onchain (self ):
175178 if self ._type in [PaymentIdentifierType .SPK , PaymentIdentifierType .MULTILINE , PaymentIdentifierType .BIP70 ,
176- PaymentIdentifierType .OPENALIAS ]:
179+ PaymentIdentifierType .OPENALIAS , PaymentIdentifierType . SILENT_PAYMENT ]:
177180 return True
178181 if self ._type in [PaymentIdentifierType .LNURLP , PaymentIdentifierType .BOLT11 , PaymentIdentifierType .LNADDR ]:
179182 return bool (self .bolt11 ) and bool (self .bolt11 .get_address ())
@@ -275,12 +278,23 @@ def parse(self, text: str):
275278 # no address, no bolt11 and no silent payment address, invalid
276279 self .set_state (PaymentIdentifierState .INVALID )
277280 return
281+ elif self .bip21 .get ('address' ) == DummyAddress .SILENT_PAYMENT :
282+ self .set_state (PaymentIdentifierState .INVALID )
283+ return # user manually entered DummyAddress.SILENT_PAYMENT
278284 self .set_state (PaymentIdentifierState .AVAILABLE )
279285 elif self .parse_output (text )[0 ]:
280286 scriptpubkey , is_address = self .parse_output (text )
281- self ._type = PaymentIdentifierType .SPK
282- self .spk = scriptpubkey
283- self .spk_is_address = is_address
287+ if bitcoin .script_to_address (scriptpubkey ) == DummyAddress .SILENT_PAYMENT :
288+ sp_address = self .parse_address (text )
289+ if sp_address == DummyAddress .SILENT_PAYMENT :
290+ self .set_state (PaymentIdentifierState .INVALID ) # user manually entered DummyAddress.SILENT_PAYMENT
291+ return
292+ self .sp_address = sp_address
293+ self ._type = PaymentIdentifierType .SILENT_PAYMENT
294+ else :
295+ self ._type = PaymentIdentifierType .SPK
296+ self .spk = scriptpubkey
297+ self .spk_is_address = is_address
284298 self .set_state (PaymentIdentifierState .AVAILABLE )
285299 elif self .contacts and (contact := self .contacts .by_name (text )):
286300 if contact ['type' ] in ('address' , 'sp_address' ):
@@ -468,23 +482,18 @@ async def _do_notify_merchant(
468482 if on_finished :
469483 on_finished (self )
470484
471- def get_onchain_outputs (self , amount , bip21_use_fallback = False ):
485+ def get_onchain_outputs (self , amount , allow_silent_payment = True ):
472486 if self .bip70 :
473487 return self .bip70_data .get_outputs ()
474488 elif self .multiline_outputs :
475489 return self .multiline_outputs
476490 elif self .spk :
477- output = PartialTxOutput (scriptpubkey = self .spk , value = amount )
478- if output .address == DummyAddress .SILENT_PAYMENT :
479- sp_addr = self .parse_address (self .text ) # parse again if e.g. a contact was entered
480- output .sp_addr = SilentPaymentAddress (sp_addr )
481- return [output ]
491+ return [PartialTxOutput (scriptpubkey = self .spk , value = amount )]
482492 elif self .bip21 :
483493 address = self .bip21 .get ('address' ) # fallback address if sp_address is present
484- sp_address = self .bip21 .get (constants .net .BIP352_HRP )
485494
486- if sp_address :
487- if bip21_use_fallback :
495+ if sp_address := self . bip21 . get ( constants . net . BIP352_HRP ) :
496+ if not allow_silent_payment :
488497 if not address :
489498 raise MissingFallbackAddress ('requested BIP21 fallback address but none was provided.' )
490499 # fallback is requested and present, keep using `address`
@@ -497,6 +506,10 @@ def get_onchain_outputs(self, amount, bip21_use_fallback=False):
497506 if output .address == DummyAddress .SILENT_PAYMENT :
498507 output .sp_addr = SilentPaymentAddress (address )
499508 return [output ]
509+ elif self .sp_address :
510+ output = PartialTxOutput .from_address_and_value (address = DummyAddress .SILENT_PAYMENT , value = amount )
511+ output .sp_addr = SilentPaymentAddress (self .sp_address )
512+ return [output ]
500513 else :
501514 raise Exception ('not onchain' )
502515
@@ -595,7 +608,7 @@ def _get_error_from_invoiceerror(self, e: 'InvoiceError') -> str:
595608 error = _ ("Invoice requires unknown or incompatible Lightning feature" ) + f":\n { e !r} "
596609 return error
597610
598- def get_fields_for_GUI (self , * , bip21_prefer_fallback = False ) -> FieldsForGUI :
611+ def get_fields_for_GUI (self ) -> FieldsForGUI :
599612 recipient = None
600613 amount = None
601614 description = None
@@ -647,8 +660,7 @@ def get_fields_for_GUI(self, *, bip21_prefer_fallback=False) -> FieldsForGUI:
647660 elif self .bip21 :
648661 label = self .bip21 .get ('label' )
649662 address = self .bip21 .get ('address' )
650- sp_address = self .bip21 .get (constants .net .BIP352_HRP )
651- if sp_address and not (address and bip21_prefer_fallback ): # return fallback address if provided and needed
663+ if sp_address := self .bip21 .get (constants .net .BIP352_HRP ):
652664 address = sp_address
653665 recipient = f'{ label } <{ address } >' if label else address
654666 amount = self .bip21 .get ('amount' )
@@ -700,7 +712,7 @@ def has_expired(self):
700712 def involves_silent_payments (self , wallet_can_send_sp = True ) -> bool :
701713 try :
702714 return any (o .is_silent_payment () for o
703- in self .get_onchain_outputs (0 , bip21_use_fallback = not wallet_can_send_sp ))
715+ in self .get_onchain_outputs (0 , allow_silent_payment = wallet_can_send_sp ))
704716 except MissingFallbackAddress :
705717 # BIP21 URI contained only a silent payment address, and the wallet cannot send to it.
706718 # Since no fallback address was provided, we treat this as involving silent payments.
@@ -726,7 +738,7 @@ def invoice_from_payment_identifier(
726738 invoice .set_amount_msat (int (amount_sat * 1000 ))
727739 return invoice
728740 else :
729- outputs = pi .get_onchain_outputs (amount_sat , bip21_use_fallback = not wallet .can_send_silent_payment ())
741+ outputs = pi .get_onchain_outputs (amount_sat , allow_silent_payment = wallet .can_send_silent_payment ())
730742 message = pi .bip21 .get ('message' ) if pi .bip21 else message
731743 bip70_data = pi .bip70_data if pi .bip70 else None
732744 return wallet .create_invoice (
0 commit comments