@@ -1554,8 +1554,9 @@ where
1554
1554
/// #
1555
1555
/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> {
1556
1556
/// # let channel_manager = channel_manager.get_cm();
1557
+ /// # let absolute_expiry = None;
1557
1558
/// let offer = channel_manager
1558
- /// .create_offer_builder()?
1559
+ /// .create_offer_builder(absolute_expiry )?
1559
1560
/// # ;
1560
1561
/// # // Needed for compiling for c_bindings
1561
1562
/// # let builder: lightning::offers::offer::OfferBuilder<_, _> = offer.into();
@@ -2287,6 +2288,19 @@ const MAX_UNFUNDED_CHANNEL_PEERS: usize = 50;
2287
2288
/// many peers we reject new (inbound) connections.
2288
2289
const MAX_NO_CHANNEL_PEERS: usize = 250;
2289
2290
2291
+ /// The maximum expiration from the current time where an [`Offer`] or [`Refund`] is considered
2292
+ /// short-lived, while anything with a greater expiration is considered long-lived.
2293
+ ///
2294
+ /// Using [`ChannelManager::create_offer_builder`] or [`ChannelManager::create_refund_builder`],
2295
+ /// will included a [`BlindedPath`] created using:
2296
+ /// - [`MessageRouter::create_compact_blinded_paths`] when short-lived, and
2297
+ /// - [`MessageRouter::create_blinded_paths`] when long-lived.
2298
+ ///
2299
+ /// Using compact [`BlindedPath`]s may provide better privacy as the [`MessageRouter`] could select
2300
+ /// more hops. However, since they use short channel ids instead of pubkeys, they are more likely to
2301
+ /// become invalid over time as channels are closed. Thus, they are only suitable for short-term use.
2302
+ pub const MAX_SHORT_LIVED_RELATIVE_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24);
2303
+
2290
2304
/// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments.
2291
2305
/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
2292
2306
#[derive(Debug, PartialEq)]
@@ -8240,16 +8254,17 @@ where
8240
8254
8241
8255
macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
8242
8256
/// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
8243
- /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
8244
- /// not have an expiration unless otherwise set on the builder .
8257
+ /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer's
8258
+ /// expiration will be `absolute_expiry` if `Some`, otherwise it will not expire .
8245
8259
///
8246
8260
/// # Privacy
8247
8261
///
8248
- /// Uses [`MessageRouter::create_compact_blinded_paths`] to construct a [`BlindedPath`] for the
8249
- /// offer. However, if one is not found, uses a one-hop [`BlindedPath`] with
8262
+ /// Uses [`MessageRouter`] to construct a [`BlindedPath`] for the offer based on the given
8263
+ /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
8264
+ /// privacy implications. However, if one is not found, uses a one-hop [`BlindedPath`] with
8250
8265
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
8251
- /// the node must be announced, otherwise, there is no way to find a path to the introduction in
8252
- /// order to send the [`InvoiceRequest`].
8266
+ /// the node must be announced, otherwise, there is no way to find a path to the introduction
8267
+ /// node in order to send the [`InvoiceRequest`].
8253
8268
///
8254
8269
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
8255
8270
///
@@ -8264,20 +8279,27 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
8264
8279
///
8265
8280
/// [`Offer`]: crate::offers::offer::Offer
8266
8281
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
8267
- pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> {
8282
+ pub fn create_offer_builder(
8283
+ &$self, absolute_expiry: Option<Duration>
8284
+ ) -> Result<$builder, Bolt12SemanticError> {
8268
8285
let node_id = $self.get_our_node_id();
8269
8286
let expanded_key = &$self.inbound_payment_key;
8270
8287
let entropy = &*$self.entropy_source;
8271
8288
let secp_ctx = &$self.secp_ctx;
8272
8289
8273
- let path = $self.create_compact_blinded_path( )
8290
+ let path = $self.create_blinded_path_using_absolute_expiry(absolute_expiry )
8274
8291
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8275
8292
let builder = OfferBuilder::deriving_signing_pubkey(
8276
8293
node_id, expanded_key, entropy, secp_ctx
8277
8294
)
8278
8295
.chain_hash($self.chain_hash)
8279
8296
.path(path);
8280
8297
8298
+ let builder = match absolute_expiry {
8299
+ None => builder,
8300
+ Some(absolute_expiry) => builder.absolute_expiry(absolute_expiry),
8301
+ };
8302
+
8281
8303
Ok(builder.into())
8282
8304
}
8283
8305
} }
@@ -8305,11 +8327,12 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
8305
8327
///
8306
8328
/// # Privacy
8307
8329
///
8308
- /// Uses [`MessageRouter::create_compact_blinded_paths`] to construct a [`BlindedPath`] for the
8309
- /// refund. However, if one is not found, uses a one-hop [`BlindedPath`] with
8330
+ /// Uses [`MessageRouter`] to construct a [`BlindedPath`] for the refund based on the given
8331
+ /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
8332
+ /// privacy implications. However, if one is not found, uses a one-hop [`BlindedPath`] with
8310
8333
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
8311
- /// the node must be announced, otherwise, there is no way to find a path to the introduction in
8312
- /// order to send the [`Bolt12Invoice`].
8334
+ /// the node must be announced, otherwise, there is no way to find a path to the introduction
8335
+ /// node in order to send the [`Bolt12Invoice`].
8313
8336
///
8314
8337
/// Also, uses a derived payer id in the refund for payer privacy.
8315
8338
///
@@ -8338,7 +8361,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
8338
8361
let entropy = &*$self.entropy_source;
8339
8362
let secp_ctx = &$self.secp_ctx;
8340
8363
8341
- let path = $self.create_compact_blinded_path( )
8364
+ let path = $self.create_blinded_path_using_absolute_expiry(Some(absolute_expiry) )
8342
8365
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
8343
8366
let builder = RefundBuilder::deriving_payer_id(
8344
8367
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
@@ -8688,6 +8711,38 @@ where
8688
8711
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
8689
8712
}
8690
8713
8714
+ /// Creates a blinded path by delegating to [`MessageRouter`] based on the path's intended
8715
+ /// lifetime.
8716
+ ///
8717
+ /// Whether or not the path is compact depends on whether the path is short-lived or long-lived,
8718
+ /// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch. See
8719
+ /// [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`].
8720
+ fn create_blinded_path_using_absolute_expiry(
8721
+ &self, absolute_expiry: Option<Duration>
8722
+ ) -> Result<BlindedPath, ()> {
8723
+ let now = self.duration_since_epoch();
8724
+ let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY);
8725
+
8726
+ if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry {
8727
+ self.create_compact_blinded_path()
8728
+ } else {
8729
+ self.create_blinded_path()
8730
+ }
8731
+ }
8732
+
8733
+ pub(super) fn duration_since_epoch(&self) -> Duration {
8734
+ #[cfg(not(feature = "std"))]
8735
+ let now = Duration::from_secs(
8736
+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
8737
+ );
8738
+ #[cfg(feature = "std")]
8739
+ let now = std::time::SystemTime::now()
8740
+ .duration_since(std::time::SystemTime::UNIX_EPOCH)
8741
+ .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
8742
+
8743
+ now
8744
+ }
8745
+
8691
8746
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
8692
8747
///
8693
8748
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
0 commit comments