Skip to content

Commit 8b2634b

Browse files
committed
Update create_refund_builder to use create_blinded_paths
This change mirrors the previous update to `create_offer_builder`, applying the **“One `MessageRouter`, one `BlindedPath` type”** principle to refund creation. Now, `create_refund_builder` uses the `create_blinded_paths` method of the `MessageRouter` associated with the `ChannelManager` or `OffersMessageFlow`. For non-default path behavior, users can call `create_refund_builder_using_router` and pass a custom `MessageRouter`. See previous commit for detailed reasoning.
1 parent 1bb6051 commit 8b2634b

File tree

2 files changed

+183
-30
lines changed

2 files changed

+183
-30
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11024,6 +11024,72 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
1102411024

1102511025
Ok(builder.into())
1102611026
}
11027+
11028+
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
11029+
/// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
11030+
///
11031+
/// # Payment
11032+
///
11033+
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
11034+
/// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
11035+
///
11036+
/// The builder will have the provided expiration set. Any changes to the expiration on the
11037+
/// returned builder will not be honored by [`ChannelManager`]. For non-`std`, the highest seen
11038+
/// block time minus two hours is used for the current time when determining if the refund has
11039+
/// expired.
11040+
///
11041+
/// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the
11042+
/// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail
11043+
/// with an [`Event::PaymentFailed`].
11044+
///
11045+
/// If `max_total_routing_fee_msat` is not specified, The default from
11046+
/// [`RouteParameters::from_payment_params_and_value`] is applied.
11047+
///
11048+
/// # Privacy
11049+
///
11050+
/// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`].
11051+
/// Users can implement a custom [`MessageRouter`] to define properties of the
11052+
/// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`.
11053+
///
11054+
/// Also, uses a derived payer id in the refund for payer privacy.
11055+
///
11056+
/// # Errors
11057+
///
11058+
/// Errors if:
11059+
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
11060+
/// - `amount_msats` is invalid, or
11061+
/// - the provided [`MessageRouter`] is unable to create a blinded path for the refund.
11062+
///
11063+
/// [`Refund`]: crate::offers::refund::Refund
11064+
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
11065+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
11066+
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
11067+
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
11068+
pub fn create_refund_builder_using_router<ME: Deref>(
11069+
&$self, router: ME, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
11070+
retry_strategy: Retry, route_params_config: RouteParametersConfig
11071+
) -> Result<$builder, Bolt12SemanticError>
11072+
where
11073+
ME::Target: MessageRouter,
11074+
{
11075+
let entropy = &*$self.entropy_source;
11076+
11077+
let builder = $self.flow.create_refund_builder_using_router(
11078+
router, entropy, amount_msats, absolute_expiry,
11079+
payment_id, $self.get_peers_for_blinded_path()
11080+
)?;
11081+
11082+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
11083+
11084+
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
11085+
$self.pending_outbound_payments
11086+
.add_new_awaiting_invoice(
11087+
payment_id, expiration, retry_strategy, route_params_config, None,
11088+
)
11089+
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
11090+
11091+
Ok(builder.into())
11092+
}
1102711093
} }
1102811094

1102911095
impl<

lightning/src/offers/flow.rs

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -622,28 +622,71 @@ where
622622
})
623623
}
624624

625+
fn create_refund_builder_intern<ES: Deref, PF, I>(
626+
&self, entropy_source: ES, make_paths: PF, amount_msats: u64, absolute_expiry: Duration,
627+
payment_id: PaymentId,
628+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError>
629+
where
630+
ES::Target: EntropySource,
631+
PF: FnOnce(
632+
PublicKey,
633+
MessageContext,
634+
&secp256k1::Secp256k1<secp256k1::All>,
635+
) -> Result<I, Bolt12SemanticError>,
636+
I: IntoIterator<Item = BlindedMessagePath>,
637+
{
638+
let node_id = self.get_our_node_id();
639+
let expanded_key = &self.inbound_payment_key;
640+
let entropy = &*entropy_source;
641+
let secp_ctx = &self.secp_ctx;
642+
643+
let nonce = Nonce::from_entropy_source(entropy);
644+
let context = MessageContext::Offers(OffersContext::OutboundPayment {
645+
payment_id,
646+
nonce,
647+
hmac: None,
648+
});
649+
650+
// Create the base builder with common properties
651+
let mut builder = RefundBuilder::deriving_signing_pubkey(
652+
node_id,
653+
expanded_key,
654+
nonce,
655+
secp_ctx,
656+
amount_msats,
657+
payment_id,
658+
)?
659+
.chain_hash(self.chain_hash)
660+
.absolute_expiry(absolute_expiry);
661+
662+
for path in make_paths(node_id, context, secp_ctx)? {
663+
builder = builder.path(path);
664+
}
665+
666+
Ok(builder.into())
667+
}
668+
625669
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
626670
/// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
627671
/// can be verified using [`Self::verify_bolt12_invoice`].
628672
///
673+
/// # Privacy
674+
///
675+
/// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
676+
/// for the offer. See those docs for privacy implications.
677+
///
629678
/// The builder will have the provided expiration set. Any changes to the expiration on the
630679
/// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
631680
/// block time minus two hours is used for the current time when determining if the refund has
632681
/// expired.
633682
///
634-
/// To refund can be revoked by the user prior to receiving the invoice.
683+
/// The refund can be revoked by the user prior to receiving the invoice.
635684
/// If abandoned, or if an invoice is not received before expiration, the payment will fail
636685
/// with an [`Event::PaymentFailed`].
637686
///
638687
/// If `max_total_routing_fee_msat` is not specified, the default from
639688
/// [`RouteParameters::from_payment_params_and_value`] is applied.
640689
///
641-
/// # Privacy
642-
///
643-
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
644-
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
645-
/// privacy implications.
646-
///
647690
/// Also uses a derived payer id in the refund for payer privacy.
648691
///
649692
/// # Errors
@@ -662,32 +705,76 @@ where
662705
where
663706
ES::Target: EntropySource,
664707
{
665-
let node_id = self.get_our_node_id();
666-
let expanded_key = &self.inbound_payment_key;
667-
let entropy = &*entropy_source;
668-
let secp_ctx = &self.secp_ctx;
669-
670-
let nonce = Nonce::from_entropy_source(entropy);
671-
let context = OffersContext::OutboundPayment { payment_id, nonce, hmac: None };
672-
673-
let path = self
674-
.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry), peers)
675-
.and_then(|paths| paths.into_iter().next().ok_or(()))
676-
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
677-
678-
let builder = RefundBuilder::deriving_signing_pubkey(
679-
node_id,
680-
expanded_key,
681-
nonce,
682-
secp_ctx,
708+
self.create_refund_builder_intern(
709+
&*entropy_source,
710+
|_, context, _| {
711+
self.create_blinded_paths(peers, context)
712+
.map(|paths| paths.into_iter().take(1))
713+
.map_err(|_| Bolt12SemanticError::MissingPaths)
714+
},
683715
amount_msats,
716+
absolute_expiry,
684717
payment_id,
685-
)?
686-
.chain_hash(self.chain_hash)
687-
.absolute_expiry(absolute_expiry)
688-
.path(path);
718+
)
719+
}
689720

690-
Ok(builder)
721+
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
722+
/// [`OffersMessageFlow`] when handling [`Bolt12Invoice`] messages for the refund.
723+
///
724+
/// # Privacy
725+
///
726+
/// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`].
727+
/// Users can implement a custom [`MessageRouter`] to define properties of the
728+
/// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`.
729+
///
730+
/// # Payment
731+
///
732+
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
733+
/// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
734+
///
735+
/// The builder will have the provided expiration set. Any changes to the expiration on the
736+
/// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
737+
/// block time minus two hours is used for the current time when determining if the refund has
738+
/// expired.
739+
///
740+
/// The refund can be revoked by the user prior to receiving the invoice.
741+
/// If abandoned, or if an invoice is not received before expiration, the payment will fail
742+
/// with an [`Event::PaymentFailed`].
743+
///
744+
/// If `max_total_routing_fee_msat` is not specified, The default from
745+
/// [`RouteParameters::from_payment_params_and_value`] is applied.
746+
///
747+
/// Also, uses a derived payer id in the refund for payer privacy.
748+
///
749+
/// # Errors
750+
///
751+
/// Errors if:
752+
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
753+
/// - `amount_msats` is invalid, or
754+
/// - the provided [`MessageRouter`] is unable to create a blinded path for the refund.
755+
///
756+
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
757+
/// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value
758+
pub fn create_refund_builder_using_router<ES: Deref, ME: Deref>(
759+
&self, router: ME, entropy_source: ES, amount_msats: u64, absolute_expiry: Duration,
760+
payment_id: PaymentId, peers: Vec<MessageForwardNode>,
761+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError>
762+
where
763+
ME::Target: MessageRouter,
764+
ES::Target: EntropySource,
765+
{
766+
self.create_refund_builder_intern(
767+
&*entropy_source,
768+
|node_id, context, secp_ctx| {
769+
router
770+
.create_blinded_paths(node_id, context, peers, secp_ctx)
771+
.map(|paths| paths.into_iter().take(1))
772+
.map_err(|_| Bolt12SemanticError::MissingPaths)
773+
},
774+
amount_msats,
775+
absolute_expiry,
776+
payment_id,
777+
)
691778
}
692779

693780
/// Creates an [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized

0 commit comments

Comments
 (0)