Skip to content

Commit d6292d1

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 ddb1167 commit d6292d1

File tree

3 files changed

+153
-32
lines changed

3 files changed

+153
-32
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11577,6 +11577,55 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
1157711577

1157811578
Ok(builder.into())
1157911579
}
11580+
11581+
/// Same as [`Self::create_refund_builder`], but allows specifying a custom [`MessageRouter`]
11582+
/// instead of using the one provided during [`ChannelManager`] construction for
11583+
/// [`BlindedMessagePath`] creation.
11584+
///
11585+
/// This gives users full control over how the [`BlindedMessagePath`] is constructed for the
11586+
/// refund, including the option to omit it entirely. This is useful for testing or when
11587+
/// alternative privacy strategies are needed.
11588+
///
11589+
/// See [`Self::create_refund_builder`] for:
11590+
/// - refund recognition by [`ChannelManager`] via [`Bolt12Invoice`] handling,
11591+
/// - `payment_id` rules and expiration behavior,
11592+
/// - invoice revocation and refund failure handling,
11593+
/// - defaulting behavior for `max_total_routing_fee_msat`,
11594+
/// - and detailed payment and privacy semantics.
11595+
///
11596+
/// # Errors
11597+
///
11598+
/// In addition to the errors in [`Self::create_refund_builder`], this returns an error if
11599+
/// the provided [`MessageRouter`] fails to construct a valid [`BlindedMessagePath`] for the refund.
11600+
///
11601+
/// [`Refund`]: crate::offers::refund::Refund
11602+
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
11603+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
11604+
pub fn create_refund_builder_using_router<ME: Deref>(
11605+
&$self, router: ME, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
11606+
retry_strategy: Retry, route_params_config: RouteParametersConfig
11607+
) -> Result<$builder, Bolt12SemanticError>
11608+
where
11609+
ME::Target: MessageRouter,
11610+
{
11611+
let entropy = &*$self.entropy_source;
11612+
11613+
let builder = $self.flow.create_refund_builder_using_router(
11614+
router, entropy, amount_msats, absolute_expiry,
11615+
payment_id, $self.get_peers_for_blinded_path()
11616+
)?;
11617+
11618+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
11619+
11620+
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
11621+
$self.pending_outbound_payments
11622+
.add_new_awaiting_invoice(
11623+
payment_id, expiration, retry_strategy, route_params_config, None,
11624+
)
11625+
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
11626+
11627+
Ok(builder.into())
11628+
}
1158011629
} }
1158111630

1158211631
impl<

lightning/src/ln/offers_tests.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ fn creates_short_lived_refund() {
467467
#[test]
468468
fn creates_long_lived_refund() {
469469
let chanmon_cfgs = create_chanmon_cfgs(2);
470-
let node_cfgs = create_node_cfgs_with_node_id_message_router(2, &chanmon_cfgs);
470+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
471471
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
472472
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
473473

@@ -479,8 +479,10 @@ fn creates_long_lived_refund() {
479479
let absolute_expiry = bob.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY
480480
+ Duration::from_secs(1);
481481
let payment_id = PaymentId([1; 32]);
482+
483+
let router = NodeIdMessageRouter::new(bob.network_graph, bob.keys_manager);
482484
let refund = bob.node
483-
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
485+
.create_refund_builder_using_router(&router, 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), RouteParametersConfig::default())
484486
.unwrap()
485487
.build().unwrap();
486488
assert_eq!(refund.absolute_expiry(), Some(absolute_expiry));

lightning/src/offers/flow.rs

Lines changed: 100 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -677,28 +677,67 @@ where
677677
})
678678
}
679679

680+
fn create_refund_builder_intern<ES: Deref, PF, I>(
681+
&self, entropy_source: ES, make_paths: PF, amount_msats: u64, absolute_expiry: Duration,
682+
payment_id: PaymentId,
683+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError>
684+
where
685+
ES::Target: EntropySource,
686+
PF: FnOnce(
687+
PublicKey,
688+
MessageContext,
689+
&secp256k1::Secp256k1<secp256k1::All>,
690+
) -> Result<I, Bolt12SemanticError>,
691+
I: IntoIterator<Item = BlindedMessagePath>,
692+
{
693+
let node_id = self.get_our_node_id();
694+
let expanded_key = &self.inbound_payment_key;
695+
let entropy = &*entropy_source;
696+
let secp_ctx = &self.secp_ctx;
697+
698+
let nonce = Nonce::from_entropy_source(entropy);
699+
let context = MessageContext::Offers(OffersContext::OutboundPayment { payment_id, nonce });
700+
701+
// Create the base builder with common properties
702+
let mut builder = RefundBuilder::deriving_signing_pubkey(
703+
node_id,
704+
expanded_key,
705+
nonce,
706+
secp_ctx,
707+
amount_msats,
708+
payment_id,
709+
)?
710+
.chain_hash(self.chain_hash)
711+
.absolute_expiry(absolute_expiry);
712+
713+
for path in make_paths(node_id, context, secp_ctx)? {
714+
builder = builder.path(path);
715+
}
716+
717+
Ok(builder.into())
718+
}
719+
680720
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
681721
/// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
682722
/// can be verified using [`Self::verify_bolt12_invoice`].
683723
///
724+
/// # Privacy
725+
///
726+
/// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
727+
/// for the offer. See those docs for privacy implications.
728+
///
684729
/// The builder will have the provided expiration set. Any changes to the expiration on the
685730
/// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
686731
/// block time minus two hours is used for the current time when determining if the refund has
687732
/// expired.
688733
///
689-
/// To refund can be revoked by the user prior to receiving the invoice.
734+
/// The refund can be revoked by the user prior to receiving the invoice.
690735
/// If abandoned, or if an invoice is not received before expiration, the payment will fail
691736
/// with an [`Event::PaymentFailed`].
692737
///
693738
/// If `max_total_routing_fee_msat` is not specified, the default from
694739
/// [`RouteParameters::from_payment_params_and_value`] is applied.
695740
///
696-
/// # Privacy
697-
///
698-
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
699-
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
700-
/// privacy implications.
701-
///
702741
/// Also uses a derived payer id in the refund for payer privacy.
703742
///
704743
/// # Errors
@@ -717,32 +756,63 @@ where
717756
where
718757
ES::Target: EntropySource,
719758
{
720-
let node_id = self.get_our_node_id();
721-
let expanded_key = &self.inbound_payment_key;
722-
let entropy = &*entropy_source;
723-
let secp_ctx = &self.secp_ctx;
724-
725-
let nonce = Nonce::from_entropy_source(entropy);
726-
let context = OffersContext::OutboundPayment { payment_id, nonce };
727-
728-
let path = self
729-
.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry), peers)
730-
.and_then(|paths| paths.into_iter().next().ok_or(()))
731-
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
732-
733-
let builder = RefundBuilder::deriving_signing_pubkey(
734-
node_id,
735-
expanded_key,
736-
nonce,
737-
secp_ctx,
759+
self.create_refund_builder_intern(
760+
&*entropy_source,
761+
|_, context, _| {
762+
self.create_blinded_paths(peers, context)
763+
.map(|paths| paths.into_iter().take(1))
764+
.map_err(|_| Bolt12SemanticError::MissingPaths)
765+
},
738766
amount_msats,
767+
absolute_expiry,
739768
payment_id,
740-
)?
741-
.chain_hash(self.chain_hash)
742-
.absolute_expiry(absolute_expiry)
743-
.path(path);
769+
)
770+
}
744771

745-
Ok(builder)
772+
/// Same as [`Self::create_refund_builder`] but allows specifying a custom [`MessageRouter`]
773+
/// instead of using the one provided via the [`OffersMessageFlow`] parameterization.
774+
///
775+
/// This gives users full control over how the [`BlindedMessagePath`] is constructed,
776+
/// including the option to omit it entirely.
777+
///
778+
/// See [`Self::create_refund_builder`] for:
779+
/// - how the resulting [`Refund`] is recognized by [`OffersMessageFlow`] and verified via [`Self::verify_bolt12_invoice`],
780+
/// - refund expiration handling,
781+
/// - rules around revocation and [`Event::PaymentFailed`] behavior,
782+
/// - and defaulting logic for `max_total_routing_fee_msat`.
783+
///
784+
/// # Errors
785+
///
786+
/// In addition to the errors documented in [`Self::create_refund_builder`], this method will
787+
/// return an error if the provided [`MessageRouter`] fails to construct a valid
788+
/// [`BlindedMessagePath`] for the refund.
789+
///
790+
/// [`Refund`]: crate::offers::refund::Refund
791+
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
792+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
793+
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
794+
/// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value
795+
pub fn create_refund_builder_using_router<ES: Deref, ME: Deref>(
796+
&self, router: ME, entropy_source: ES, amount_msats: u64, absolute_expiry: Duration,
797+
payment_id: PaymentId, peers: Vec<MessageForwardNode>,
798+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError>
799+
where
800+
ME::Target: MessageRouter,
801+
ES::Target: EntropySource,
802+
{
803+
let receive_key = self.get_receive_auth_key();
804+
self.create_refund_builder_intern(
805+
&*entropy_source,
806+
|node_id, context, secp_ctx| {
807+
router
808+
.create_blinded_paths(node_id, receive_key, context, peers, secp_ctx)
809+
.map(|paths| paths.into_iter().take(1))
810+
.map_err(|_| Bolt12SemanticError::MissingPaths)
811+
},
812+
amount_msats,
813+
absolute_expiry,
814+
payment_id,
815+
)
746816
}
747817

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

0 commit comments

Comments
 (0)