@@ -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,15 @@ 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_blinded_paths`] to construct a [`BlindedPath`] for the offer.
8249
- /// However, if one is not found, uses a one-hop [`BlindedPath`] with
8250
- /// [`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`].
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 as well as those of the parameterized [`Router`], which implements
8265
+ /// [`MessageRouter`].
8253
8266
///
8254
8267
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
8255
8268
///
@@ -8264,19 +8277,27 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
8264
8277
///
8265
8278
/// [`Offer`]: crate::offers::offer::Offer
8266
8279
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
8267
- pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> {
8280
+ pub fn create_offer_builder(
8281
+ &$self, absolute_expiry: Option<Duration>
8282
+ ) -> Result<$builder, Bolt12SemanticError> {
8268
8283
let node_id = $self.get_our_node_id();
8269
8284
let expanded_key = &$self.inbound_payment_key;
8270
8285
let entropy = &*$self.entropy_source;
8271
8286
let secp_ctx = &$self.secp_ctx;
8272
8287
8273
- let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8288
+ let path = $self.create_blinded_path_using_absolute_expiry(absolute_expiry)
8289
+ .map_err(|_| Bolt12SemanticError::MissingPaths)?;
8274
8290
let builder = OfferBuilder::deriving_signing_pubkey(
8275
8291
node_id, expanded_key, entropy, secp_ctx
8276
8292
)
8277
8293
.chain_hash($self.chain_hash)
8278
8294
.path(path);
8279
8295
8296
+ let builder = match absolute_expiry {
8297
+ None => builder,
8298
+ Some(absolute_expiry) => builder.absolute_expiry(absolute_expiry),
8299
+ };
8300
+
8280
8301
Ok(builder.into())
8281
8302
}
8282
8303
} }
@@ -8304,11 +8325,10 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
8304
8325
///
8305
8326
/// # Privacy
8306
8327
///
8307
- /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
8308
- /// However, if one is not found, uses a one-hop [`BlindedPath`] with
8309
- /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
8310
- /// the node must be announced, otherwise, there is no way to find a path to the introduction in
8311
- /// order to send the [`Bolt12Invoice`].
8328
+ /// Uses [`MessageRouter`] to construct a [`BlindedPath`] for the refund based on the given
8329
+ /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
8330
+ /// privacy implications as well as those of the parameterized [`Router`], which implements
8331
+ /// [`MessageRouter`].
8312
8332
///
8313
8333
/// Also, uses a derived payer id in the refund for payer privacy.
8314
8334
///
@@ -8337,7 +8357,8 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
8337
8357
let entropy = &*$self.entropy_source;
8338
8358
let secp_ctx = &$self.secp_ctx;
8339
8359
8340
- let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
8360
+ let path = $self.create_blinded_path_using_absolute_expiry(Some(absolute_expiry))
8361
+ .map_err(|_| Bolt12SemanticError::MissingPaths)?;
8341
8362
let builder = RefundBuilder::deriving_payer_id(
8342
8363
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
8343
8364
)?
@@ -8406,10 +8427,9 @@ where
8406
8427
///
8407
8428
/// # Privacy
8408
8429
///
8409
- /// Uses a one-hop [`BlindedPath`] for the reply path with [`ChannelManager::get_our_node_id`]
8410
- /// as the introduction node and a derived payer id for payer privacy. As such, currently, the
8411
- /// node must be announced. Otherwise, there is no way to find a path to the introduction node
8412
- /// in order to send the [`Bolt12Invoice`].
8430
+ /// For payer privacy, uses a derived payer id and uses [`MessageRouter::create_blinded_paths`]
8431
+ /// to construct a [`BlindedPath`] for the reply path. For further privacy implications, see the
8432
+ /// docs of the parameterized [`Router`], which implements [`MessageRouter`].
8413
8433
///
8414
8434
/// # Limitations
8415
8435
///
@@ -8686,6 +8706,38 @@ where
8686
8706
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
8687
8707
}
8688
8708
8709
+ /// Creates a blinded path by delegating to [`MessageRouter`] based on the path's intended
8710
+ /// lifetime.
8711
+ ///
8712
+ /// Whether or not the path is compact depends on whether the path is short-lived or long-lived,
8713
+ /// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch. See
8714
+ /// [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`].
8715
+ fn create_blinded_path_using_absolute_expiry(
8716
+ &self, absolute_expiry: Option<Duration>
8717
+ ) -> Result<BlindedPath, ()> {
8718
+ let now = self.duration_since_epoch();
8719
+ let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY);
8720
+
8721
+ if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry {
8722
+ self.create_compact_blinded_path()
8723
+ } else {
8724
+ self.create_blinded_path()
8725
+ }
8726
+ }
8727
+
8728
+ pub(super) fn duration_since_epoch(&self) -> Duration {
8729
+ #[cfg(not(feature = "std"))]
8730
+ let now = Duration::from_secs(
8731
+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
8732
+ );
8733
+ #[cfg(feature = "std")]
8734
+ let now = std::time::SystemTime::now()
8735
+ .duration_since(std::time::SystemTime::UNIX_EPOCH)
8736
+ .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
8737
+
8738
+ now
8739
+ }
8740
+
8689
8741
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
8690
8742
///
8691
8743
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
@@ -8696,6 +8748,27 @@ where
8696
8748
let peers = self.per_peer_state.read().unwrap()
8697
8749
.iter()
8698
8750
.map(|(node_id, peer_state)| (node_id, peer_state.lock().unwrap()))
8751
+ .filter(|(_, peer)| peer.is_connected)
8752
+ .filter(|(_, peer)| peer.latest_features.supports_onion_messages())
8753
+ .map(|(node_id, _)| *node_id)
8754
+ .collect::<Vec<_>>();
8755
+
8756
+ self.router
8757
+ .create_blinded_paths(recipient, peers, secp_ctx)
8758
+ .and_then(|paths| paths.into_iter().next().ok_or(()))
8759
+ }
8760
+
8761
+ /// Creates a blinded path by delegating to [`MessageRouter::create_compact_blinded_paths`].
8762
+ ///
8763
+ /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
8764
+ fn create_compact_blinded_path(&self) -> Result<BlindedPath, ()> {
8765
+ let recipient = self.get_our_node_id();
8766
+ let secp_ctx = &self.secp_ctx;
8767
+
8768
+ let peers = self.per_peer_state.read().unwrap()
8769
+ .iter()
8770
+ .map(|(node_id, peer_state)| (node_id, peer_state.lock().unwrap()))
8771
+ .filter(|(_, peer)| peer.is_connected)
8699
8772
.filter(|(_, peer)| peer.latest_features.supports_onion_messages())
8700
8773
.map(|(node_id, peer)| ForwardNode {
8701
8774
node_id: *node_id,
@@ -8708,7 +8781,7 @@ where
8708
8781
.collect::<Vec<_>>();
8709
8782
8710
8783
self.router
8711
- .create_blinded_paths (recipient, peers, secp_ctx)
8784
+ .create_compact_blinded_paths (recipient, peers, secp_ctx)
8712
8785
.and_then(|paths| paths.into_iter().next().ok_or(()))
8713
8786
}
8714
8787
0 commit comments