@@ -572,28 +572,70 @@ where
572572 } )
573573 }
574574
575+ fn create_refund_builder_intern < PF , ES : Deref > (
576+ & self , make_paths : PF , entropy_source : ES , amount_msats : u64 , absolute_expiry : Duration ,
577+ payment_id : PaymentId ,
578+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
579+ where
580+ PF : FnOnce (
581+ PublicKey ,
582+ MessageContext ,
583+ & secp256k1:: Secp256k1 < secp256k1:: All > ,
584+ ) -> Result < Vec < BlindedMessagePath > , Bolt12SemanticError > ,
585+ ES :: Target : EntropySource ,
586+ {
587+ let node_id = self . get_our_node_id ( ) ;
588+ let expanded_key = & self . inbound_payment_key ;
589+ let entropy = & * entropy_source;
590+ let secp_ctx = & self . secp_ctx ;
591+
592+ let nonce = Nonce :: from_entropy_source ( entropy) ;
593+ let context = MessageContext :: Offers ( OffersContext :: OutboundPayment {
594+ payment_id,
595+ nonce,
596+ hmac : None ,
597+ } ) ;
598+
599+ // Create the base builder with common properties
600+ let mut builder = RefundBuilder :: deriving_signing_pubkey (
601+ node_id,
602+ expanded_key,
603+ nonce,
604+ secp_ctx,
605+ amount_msats,
606+ payment_id,
607+ ) ?
608+ . chain_hash ( self . chain_hash )
609+ . absolute_expiry ( absolute_expiry) ;
610+
611+ for path in make_paths ( node_id, context, secp_ctx) ? {
612+ builder = builder. path ( path) ;
613+ }
614+
615+ Ok ( builder. into ( ) )
616+ }
617+
575618 /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
576619 /// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
577620 /// can be verified using [`Self::verify_bolt12_invoice`].
578621 ///
622+ /// # Privacy
623+ ///
624+ /// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
625+ /// for the offer. See those docs for privacy implications.
626+ ///
579627 /// The builder will have the provided expiration set. Any changes to the expiration on the
580628 /// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
581629 /// block time minus two hours is used for the current time when determining if the refund has
582630 /// expired.
583631 ///
584- /// To refund can be revoked by the user prior to receiving the invoice.
632+ /// The refund can be revoked by the user prior to receiving the invoice.
585633 /// If abandoned, or if an invoice is not received before expiration, the payment will fail
586634 /// with an [`Event::PaymentFailed`].
587635 ///
588636 /// If `max_total_routing_fee_msat` is not specified, the default from
589637 /// [`RouteParameters::from_payment_params_and_value`] is applied.
590638 ///
591- /// # Privacy
592- ///
593- /// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
594- /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
595- /// privacy implications.
596- ///
597639 /// Also uses a derived payer id in the refund for payer privacy.
598640 ///
599641 /// # Errors
@@ -612,32 +654,76 @@ where
612654 where
613655 ES :: Target : EntropySource ,
614656 {
615- let node_id = self . get_our_node_id ( ) ;
616- let expanded_key = & self . inbound_payment_key ;
617- let entropy = & * entropy_source;
618- let secp_ctx = & self . secp_ctx ;
619-
620- let nonce = Nonce :: from_entropy_source ( entropy) ;
621- let context = OffersContext :: OutboundPayment { payment_id, nonce, hmac : None } ;
622-
623- let path = self
624- . create_blinded_paths_using_absolute_expiry ( context, Some ( absolute_expiry) , peers)
625- . and_then ( |paths| paths. into_iter ( ) . next ( ) . ok_or ( ( ) ) )
626- . map_err ( |_| Bolt12SemanticError :: MissingPaths ) ?;
627-
628- let builder = RefundBuilder :: deriving_signing_pubkey (
629- node_id,
630- expanded_key,
631- nonce,
632- secp_ctx,
657+ self . create_refund_builder_intern (
658+ |_, context, _| {
659+ self . create_blinded_paths ( peers, context)
660+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) . collect ( ) )
661+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
662+ } ,
663+ & * entropy_source,
633664 amount_msats,
665+ absolute_expiry,
634666 payment_id,
635- ) ?
636- . chain_hash ( self . chain_hash )
637- . absolute_expiry ( absolute_expiry)
638- . path ( path) ;
667+ )
668+ }
639669
640- Ok ( builder)
670+ /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
671+ /// [`OffersMessageFlow`] when handling [`Bolt12Invoice`] messages for the refund.
672+ ///
673+ /// # Privacy
674+ ///
675+ /// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`].
676+ /// Users can implement a custom [`MessageRouter`] to define properties of the
677+ /// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`.
678+ ///
679+ /// # Payment
680+ ///
681+ /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
682+ /// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
683+ ///
684+ /// The builder will have the provided expiration set. Any changes to the expiration on the
685+ /// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
686+ /// block time minus two hours is used for the current time when determining if the refund has
687+ /// expired.
688+ ///
689+ /// The refund can be revoked by the user prior to receiving the invoice.
690+ /// If abandoned, or if an invoice is not received before expiration, the payment will fail
691+ /// with an [`Event::PaymentFailed`].
692+ ///
693+ /// If `max_total_routing_fee_msat` is not specified, The default from
694+ /// [`RouteParameters::from_payment_params_and_value`] is applied.
695+ ///
696+ /// Also, uses a derived payer id in the refund for payer privacy.
697+ ///
698+ /// # Errors
699+ ///
700+ /// Errors if:
701+ /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
702+ /// - `amount_msats` is invalid, or
703+ /// - the provided [`MessageRouter`] is unable to create a blinded path for the refund.
704+ ///
705+ /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
706+ /// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value
707+ pub fn create_refund_builder_using_router < ES : Deref , ME : Deref > (
708+ & self , router : ME , entropy_source : ES , amount_msats : u64 , absolute_expiry : Duration ,
709+ payment_id : PaymentId , peers : Vec < MessageForwardNode > ,
710+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
711+ where
712+ ME :: Target : MessageRouter ,
713+ ES :: Target : EntropySource ,
714+ {
715+ self . create_refund_builder_intern (
716+ |node_id, context, secp_ctx| {
717+ router
718+ . create_blinded_paths ( node_id, context, peers, secp_ctx)
719+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) . collect ( ) )
720+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
721+ } ,
722+ & * entropy_source,
723+ amount_msats,
724+ absolute_expiry,
725+ payment_id,
726+ )
641727 }
642728
643729 /// Creates an [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized
0 commit comments