Skip to content

Commit 05f9574

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 8197a99 commit 05f9574

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
@@ -10975,6 +10975,72 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
1097510975

1097610976
Ok(builder.into())
1097710977
}
10978+
10979+
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
10980+
/// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
10981+
///
10982+
/// # Payment
10983+
///
10984+
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
10985+
/// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
10986+
///
10987+
/// The builder will have the provided expiration set. Any changes to the expiration on the
10988+
/// returned builder will not be honored by [`ChannelManager`]. For non-`std`, the highest seen
10989+
/// block time minus two hours is used for the current time when determining if the refund has
10990+
/// expired.
10991+
///
10992+
/// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the
10993+
/// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail
10994+
/// with an [`Event::PaymentFailed`].
10995+
///
10996+
/// If `max_total_routing_fee_msat` is not specified, The default from
10997+
/// [`RouteParameters::from_payment_params_and_value`] is applied.
10998+
///
10999+
/// # Privacy
11000+
///
11001+
/// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`].
11002+
/// Users can implement a custom [`MessageRouter`] to define properties of the
11003+
/// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`.
11004+
///
11005+
/// Also, uses a derived payer id in the refund for payer privacy.
11006+
///
11007+
/// # Errors
11008+
///
11009+
/// Errors if:
11010+
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
11011+
/// - `amount_msats` is invalid, or
11012+
/// - the provided [`MessageRouter`] is unable to create a blinded path for the refund.
11013+
///
11014+
/// [`Refund`]: crate::offers::refund::Refund
11015+
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
11016+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
11017+
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
11018+
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
11019+
pub fn create_refund_builder_using_router<ME: Deref>(
11020+
&$self, router: ME, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
11021+
retry_strategy: Retry, route_params_config: RouteParametersConfig
11022+
) -> Result<$builder, Bolt12SemanticError>
11023+
where
11024+
ME::Target: MessageRouter,
11025+
{
11026+
let entropy = &*$self.entropy_source;
11027+
11028+
let builder = $self.flow.create_refund_builder_using_router(
11029+
router, entropy, amount_msats, absolute_expiry,
11030+
payment_id, $self.get_peers_for_blinded_path()
11031+
)?;
11032+
11033+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
11034+
11035+
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
11036+
$self.pending_outbound_payments
11037+
.add_new_awaiting_invoice(
11038+
payment_id, expiration, retry_strategy, route_params_config, None,
11039+
)
11040+
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
11041+
11042+
Ok(builder.into())
11043+
}
1097811044
} }
1097911045

1098011046
impl<

lightning/src/offers/flow.rs

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -571,28 +571,71 @@ where
571571
})
572572
}
573573

574+
fn create_refund_builder_intern<ES: Deref, PF, I>(
575+
&self, entropy_source: ES, make_paths: PF, amount_msats: u64, absolute_expiry: Duration,
576+
payment_id: PaymentId,
577+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError>
578+
where
579+
ES::Target: EntropySource,
580+
PF: FnOnce(
581+
PublicKey,
582+
MessageContext,
583+
&secp256k1::Secp256k1<secp256k1::All>,
584+
) -> Result<I, Bolt12SemanticError>,
585+
I: IntoIterator<Item = BlindedMessagePath>,
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+
574618
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
575619
/// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
576620
/// can be verified using [`Self::verify_bolt12_invoice`].
577621
///
622+
/// # Privacy
623+
///
624+
/// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
625+
/// for the offer. See those docs for privacy implications.
626+
///
578627
/// The builder will have the provided expiration set. Any changes to the expiration on the
579628
/// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
580629
/// block time minus two hours is used for the current time when determining if the refund has
581630
/// expired.
582631
///
583-
/// 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.
584633
/// If abandoned, or if an invoice is not received before expiration, the payment will fail
585634
/// with an [`Event::PaymentFailed`].
586635
///
587636
/// If `max_total_routing_fee_msat` is not specified, the default from
588637
/// [`RouteParameters::from_payment_params_and_value`] is applied.
589638
///
590-
/// # Privacy
591-
///
592-
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
593-
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
594-
/// privacy implications.
595-
///
596639
/// Also uses a derived payer id in the refund for payer privacy.
597640
///
598641
/// # Errors
@@ -611,32 +654,76 @@ where
611654
where
612655
ES::Target: EntropySource,
613656
{
614-
let node_id = self.get_our_node_id();
615-
let expanded_key = &self.inbound_payment_key;
616-
let entropy = &*entropy_source;
617-
let secp_ctx = &self.secp_ctx;
618-
619-
let nonce = Nonce::from_entropy_source(entropy);
620-
let context = OffersContext::OutboundPayment { payment_id, nonce, hmac: None };
621-
622-
let path = self
623-
.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry), peers)
624-
.and_then(|paths| paths.into_iter().next().ok_or(()))
625-
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
626-
627-
let builder = RefundBuilder::deriving_signing_pubkey(
628-
node_id,
629-
expanded_key,
630-
nonce,
631-
secp_ctx,
657+
self.create_refund_builder_intern(
658+
&*entropy_source,
659+
|_, context, _| {
660+
self.create_blinded_paths(peers, context)
661+
.map(|paths| paths.into_iter().take(1))
662+
.map_err(|_| Bolt12SemanticError::MissingPaths)
663+
},
632664
amount_msats,
665+
absolute_expiry,
633666
payment_id,
634-
)?
635-
.chain_hash(self.chain_hash)
636-
.absolute_expiry(absolute_expiry)
637-
.path(path);
667+
)
668+
}
638669

639-
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+
&*entropy_source,
717+
|node_id, context, secp_ctx| {
718+
router
719+
.create_blinded_paths(node_id, context, peers, secp_ctx)
720+
.map(|paths| paths.into_iter().take(1))
721+
.map_err(|_| Bolt12SemanticError::MissingPaths)
722+
},
723+
amount_msats,
724+
absolute_expiry,
725+
payment_id,
726+
)
640727
}
641728

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

0 commit comments

Comments
 (0)