Skip to content

Commit d8f306f

Browse files
committed
Use compact blinded paths for short-lived offers
When an offer is short-lived, the likelihood of a channel used in a compact blinded path going away is low. Require passing the absolute expiry of an offer to ChannelManager::create_offer_builder so that it can be used to determine whether or not compact blinded path should be used. Use the same criteria for creating blinded paths for refunds as well.
1 parent 5c28c67 commit d8f306f

File tree

3 files changed

+191
-38
lines changed

3 files changed

+191
-38
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8542,16 +8542,15 @@ where
85428542

85438543
macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
85448544
/// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
8545-
/// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
8546-
/// not have an expiration unless otherwise set on the builder.
8545+
/// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer's
8546+
/// expiration will be `absolute_expiry` if `Some`, otherwise it will not expire.
85478547
///
85488548
/// # Privacy
85498549
///
8550-
/// Uses [`MessageRouter::create_compact_blinded_paths`] to construct a [`BlindedPath`] for the
8551-
/// offer. However, if one is not found, uses a one-hop [`BlindedPath`] with
8552-
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
8553-
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
8554-
/// order to send the [`InvoiceRequest`].
8550+
/// Uses [`MessageRouter`] to construct a [`BlindedPath`] for the offer. However, if one is not
8551+
/// found, uses a one-hop [`BlindedPath`] with [`ChannelManager::get_our_node_id`] as the
8552+
/// introduction node instead. In the latter case, the node must be announced, otherwise, there
8553+
/// is no way to find a path to the introduction in order to send the [`InvoiceRequest`].
85558554
///
85568555
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
85578556
///
@@ -8566,20 +8565,27 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
85668565
///
85678566
/// [`Offer`]: crate::offers::offer::Offer
85688567
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
8569-
pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> {
8568+
pub fn create_offer_builder(
8569+
&$self, absolute_expiry: Option<Duration>
8570+
) -> Result<$builder, Bolt12SemanticError> {
85708571
let node_id = $self.get_our_node_id();
85718572
let expanded_key = &$self.inbound_payment_key;
85728573
let entropy = &*$self.entropy_source;
85738574
let secp_ctx = &$self.secp_ctx;
85748575

8575-
let path = $self.create_compact_blinded_path()
8576+
let path = $self.create_blinded_path_using_absolute_expiry(absolute_expiry)
85768577
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
85778578
let builder = OfferBuilder::deriving_signing_pubkey(
85788579
node_id, expanded_key, entropy, secp_ctx
85798580
)
85808581
.chain_hash($self.chain_hash)
85818582
.path(path);
85828583

8584+
let builder = match absolute_expiry {
8585+
None => builder,
8586+
Some(absolute_expiry) => builder.absolute_expiry(absolute_expiry),
8587+
};
8588+
85838589
Ok(builder.into())
85848590
}
85858591
} }
@@ -8607,11 +8613,10 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
86078613
///
86088614
/// # Privacy
86098615
///
8610-
/// Uses [`MessageRouter::create_compact_blinded_paths`] to construct a [`BlindedPath`] for the
8611-
/// refund. However, if one is not found, uses a one-hop [`BlindedPath`] with
8612-
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
8613-
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
8614-
/// order to send the [`Bolt12Invoice`].
8616+
/// Uses [`MessageRouter`] to construct a [`BlindedPath`] for the refund. However, if one is not
8617+
/// found, uses a one-hop [`BlindedPath`] with [`ChannelManager::get_our_node_id`] as the
8618+
/// introduction node instead. In the latter case, the node must be announced, otherwise, there
8619+
/// is no way to find a path to the introduction in order to send the [`Bolt12Invoice`].
86158620
///
86168621
/// Also, uses a derived payer id in the refund for payer privacy.
86178622
///
@@ -8640,7 +8645,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
86408645
let entropy = &*$self.entropy_source;
86418646
let secp_ctx = &$self.secp_ctx;
86428647

8643-
let path = $self.create_compact_blinded_path()
8648+
let path = $self.create_blinded_path_using_absolute_expiry(Some(absolute_expiry))
86448649
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
86458650
let builder = RefundBuilder::deriving_payer_id(
86468651
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
@@ -8990,6 +8995,33 @@ where
89908995
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
89918996
}
89928997

8998+
/// Creates a blinded path by delegating to [`MessageRouter`] based on the path's intended
8999+
/// lifetime.
9000+
///
9001+
/// Whether or not the path is compact depends on whether the path is short-lived or long-lived,
9002+
/// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch.
9003+
fn create_blinded_path_using_absolute_expiry(
9004+
&self, absolute_expiry: Option<Duration>
9005+
) -> Result<BlindedPath, ()> {
9006+
const MAX_SHORT_LIVED_RELATIVE_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24);
9007+
9008+
#[cfg(not(feature = "std"))]
9009+
let now = Duration::from_secs(
9010+
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
9011+
);
9012+
#[cfg(feature = "std")]
9013+
let now = std::time::SystemTime::now()
9014+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
9015+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
9016+
9017+
let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY);
9018+
if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry {
9019+
self.create_compact_blinded_path()
9020+
} else {
9021+
self.create_blinded_path()
9022+
}
9023+
}
9024+
89939025
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
89949026
///
89959027
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.

lightning/src/ln/functional_test_utils.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use core::cell::RefCell;
5050
use core::iter::repeat;
5151
use core::mem;
5252
use core::ops::Deref;
53+
use core::time::Duration;
5354
use crate::io;
5455
use crate::prelude::*;
5556
use crate::sync::{Arc, Mutex, LockTestExt, RwLock};
@@ -464,6 +465,19 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
464465
self.override_init_features.borrow().clone()
465466
.unwrap_or_else(|| self.node.init_features() | self.onion_messenger.provided_init_features(peer_node_id))
466467
}
468+
469+
pub fn duration_since_epoch(&self) -> Duration {
470+
#[cfg(not(feature = "std"))]
471+
let now = Duration::from_secs(
472+
self.node.highest_seen_timestamp.load(Ordering::Acquire) as u64
473+
);
474+
#[cfg(feature = "std")]
475+
let now = std::time::SystemTime::now()
476+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
477+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
478+
479+
now
480+
}
467481
}
468482

469483
#[cfg(feature = "std")]

0 commit comments

Comments
 (0)