@@ -1554,8 +1554,9 @@ where
15541554/// #
15551555/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> {
15561556/// # let channel_manager = channel_manager.get_cm();
1557+ /// # let absolute_expiry = None;
15571558/// let offer = channel_manager
1558- /// .create_offer_builder()?
1559+ /// .create_offer_builder(absolute_expiry )?
15591560/// # ;
15601561/// # // Needed for compiling for c_bindings
15611562/// # let builder: lightning::offers::offer::OfferBuilder<_, _> = offer.into();
@@ -2287,6 +2288,19 @@ const MAX_UNFUNDED_CHANNEL_PEERS: usize = 50;
22872288/// many peers we reject new (inbound) connections.
22882289const MAX_NO_CHANNEL_PEERS: usize = 250;
22892290
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+
22902304/// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments.
22912305/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
22922306#[derive(Debug, PartialEq)]
@@ -8240,16 +8254,15 @@ where
82408254
82418255macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
82428256 /// 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 .
82458259 ///
82468260 /// # Privacy
82478261 ///
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`].
82538266 ///
82548267 /// Also, uses a derived signing pubkey in the offer for recipient privacy.
82558268 ///
@@ -8264,19 +8277,27 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
82648277 ///
82658278 /// [`Offer`]: crate::offers::offer::Offer
82668279 /// [`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> {
82688283 let node_id = $self.get_our_node_id();
82698284 let expanded_key = &$self.inbound_payment_key;
82708285 let entropy = &*$self.entropy_source;
82718286 let secp_ctx = &$self.secp_ctx;
82728287
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)?;
82748290 let builder = OfferBuilder::deriving_signing_pubkey(
82758291 node_id, expanded_key, entropy, secp_ctx
82768292 )
82778293 .chain_hash($self.chain_hash)
82788294 .path(path);
82798295
8296+ let builder = match absolute_expiry {
8297+ None => builder,
8298+ Some(absolute_expiry) => builder.absolute_expiry(absolute_expiry),
8299+ };
8300+
82808301 Ok(builder.into())
82818302 }
82828303} }
@@ -8304,11 +8325,10 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
83048325 ///
83058326 /// # Privacy
83068327 ///
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`].
83128332 ///
83138333 /// Also, uses a derived payer id in the refund for payer privacy.
83148334 ///
@@ -8337,7 +8357,8 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
83378357 let entropy = &*$self.entropy_source;
83388358 let secp_ctx = &$self.secp_ctx;
83398359
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)?;
83418362 let builder = RefundBuilder::deriving_payer_id(
83428363 node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
83438364 )?
@@ -8406,10 +8427,9 @@ where
84068427 ///
84078428 /// # Privacy
84088429 ///
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`].
84138433 ///
84148434 /// # Limitations
84158435 ///
@@ -8686,6 +8706,38 @@ where
86868706 inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
86878707 }
86888708
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+
86898741 /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
86908742 ///
86918743 /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
@@ -8696,6 +8748,27 @@ where
86968748 let peers = self.per_peer_state.read().unwrap()
86978749 .iter()
86988750 .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)
86998772 .filter(|(_, peer)| peer.latest_features.supports_onion_messages())
87008773 .map(|(node_id, peer)| ForwardNode {
87018774 node_id: *node_id,
@@ -8708,7 +8781,7 @@ where
87088781 .collect::<Vec<_>>();
87098782
87108783 self.router
8711- .create_blinded_paths (recipient, peers, secp_ctx)
8784+ .create_compact_blinded_paths (recipient, peers, secp_ctx)
87128785 .and_then(|paths| paths.into_iter().next().ok_or(()))
87138786 }
87148787
0 commit comments