Skip to content

Commit ac6e640

Browse files
Util for blinded paths to configure an async recipient
As part of serving static invoices to payers on behalf of often-offline recipients, these recipients need a way to contact the static invoice server to retrieve blinded paths to include in their offers. Add a utility to create blinded paths for this purpose as a static invoice server. The recipient will be configured with the resulting paths and use them to request offer paths on startup.
1 parent 182a1e3 commit ac6e640

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,34 @@ pub enum OffersContext {
394394
/// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage
395395
#[derive(Clone, Debug)]
396396
pub enum AsyncPaymentsContext {
397+
/// Context used by a [`BlindedMessagePath`] that an async recipient is configured with in
398+
/// [`UserConfig::paths_to_static_invoice_server`], provided back to us in corresponding
399+
/// [`OfferPathsRequest`]s.
400+
///
401+
/// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
402+
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
403+
OfferPathsRequest {
404+
/// An identifier for the async recipient that is requesting blinded paths to include in their
405+
/// [`Offer::paths`]. This ID is intended to be included in the reply path to our [`OfferPaths`]
406+
/// response, and subsequently rate limit [`ServeStaticInvoice`] messages from recipients.
407+
///
408+
/// [`Offer::paths`]: crate::offers::offer::Offer::paths
409+
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
410+
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
411+
recipient_id_nonce: Nonce,
412+
/// Authentication code for the [`OfferPathsRequest`].
413+
///
414+
/// Prevents nodes from requesting offer paths from us without having been previously configured
415+
/// with a [`BlindedMessagePath`] that we generated.
416+
///
417+
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
418+
hmac: Hmac<Sha256>,
419+
/// The time as duration since the Unix epoch at which this path expires and messages sent over
420+
/// it should be ignored.
421+
///
422+
/// Useful to timeout async recipients that are no longer supported as clients.
423+
path_absolute_expiry: core::time::Duration,
424+
},
397425
/// Context used by a reply path to an [`OfferPathsRequest`], provided back to us in corresponding
398426
/// [`OfferPaths`] messages.
399427
///
@@ -545,6 +573,11 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
545573
(4, hmac, required),
546574
(6, path_absolute_expiry, required),
547575
},
576+
(4, OfferPathsRequest) => {
577+
(0, recipient_id_nonce, required),
578+
(2, hmac, required),
579+
(4, path_absolute_expiry, required),
580+
},
548581
);
549582

550583
/// Contains a simple nonce for use in a blinded path's context.

lightning/src/ln/channelmanager.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ use crate::util::errors::APIError;
9494
#[cfg(async_payments)] use {
9595
crate::offers::offer::Amount,
9696
crate::offers::static_invoice::{DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY, StaticInvoice, StaticInvoiceBuilder},
97-
crate::onion_message::async_payments::REPLY_PATH_RELATIVE_EXPIRY,
97+
crate::onion_message::async_payments::{DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY, REPLY_PATH_RELATIVE_EXPIRY},
9898
};
9999

100100
#[cfg(feature = "dnssec")]
@@ -10825,6 +10825,39 @@ where
1082510825
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
1082610826
}
1082710827

10828+
/// [`BlindedMessagePath`]s that an async recipient will be configured with via
10829+
/// [`UserConfig::paths_to_static_invoice_server`], enabling the recipient to request blinded
10830+
/// paths from us for inclusion in their [`Offer::paths`].
10831+
///
10832+
/// If `relative_expiry` is unset, the [`BlindedMessagePath`]s expiry will default to
10833+
/// [`DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY`].
10834+
///
10835+
/// Returns the paths to be included in the recipient's
10836+
/// [`UserConfig::paths_to_static_invoice_server`] as well as a nonce that uniquely identifies the
10837+
/// recipient that has been configured with these paths. // TODO link to events that surface this nonce
10838+
///
10839+
/// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
10840+
/// [`Offer::paths`]: crate::offers::offer::Offer::paths
10841+
/// [`DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY`]: crate::onion_message::async_payments::DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY
10842+
#[cfg(async_payments)]
10843+
pub fn blinded_paths_for_async_recipient(
10844+
&self, relative_expiry: Option<Duration>
10845+
) -> Result<(Vec<BlindedMessagePath>, Nonce), ()> {
10846+
let expanded_key = &self.inbound_payment_key;
10847+
let entropy = &*self.entropy_source;
10848+
10849+
let path_absolute_expiry = relative_expiry.unwrap_or(DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY)
10850+
.saturating_add(self.duration_since_epoch());
10851+
10852+
let recipient_id_nonce = Nonce::from_entropy_source(entropy);
10853+
let hmac = signer::hmac_for_offer_paths_request_context(recipient_id_nonce, expanded_key);
10854+
10855+
let context = MessageContext::AsyncPayments(
10856+
AsyncPaymentsContext::OfferPathsRequest { recipient_id_nonce, hmac, path_absolute_expiry }
10857+
);
10858+
self.create_blinded_paths(context).map(|paths| (paths, recipient_id_nonce))
10859+
}
10860+
1082810861
/// Creates a collection of blinded paths by delegating to [`MessageRouter`] based on
1082910862
/// the path's intended lifetime.
1083010863
///

lightning/src/offers/signer.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ const ASYNC_PAYMENTS_OFFER_PATHS_INPUT: &[u8; 16] = &[10; 16];
6565
#[cfg(async_payments)]
6666
const ASYNC_PAYMENTS_STATIC_INV_PERSISTED_INPUT: &[u8; 16] = &[11; 16];
6767

68+
///
69+
#[cfg(async_payments)]
70+
const ASYNC_PAYMENTS_OFFER_PATHS_REQUEST_INPUT: &[u8; 16] = &[12; 16];
71+
6872
/// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
6973
/// verified.
7074
#[derive(Clone)]
@@ -566,6 +570,19 @@ pub(crate) fn verify_held_htlc_available_context(
566570
}
567571
}
568572

573+
#[cfg(async_payments)]
574+
pub(crate) fn hmac_for_offer_paths_request_context(
575+
nonce: Nonce, expanded_key: &ExpandedKey,
576+
) -> Hmac<Sha256> {
577+
const IV_BYTES: &[u8; IV_LEN] = b"LDK Paths Please"; // TODO
578+
let mut hmac = expanded_key.hmac_for_offer();
579+
hmac.input(IV_BYTES);
580+
hmac.input(&nonce.0);
581+
hmac.input(ASYNC_PAYMENTS_OFFER_PATHS_REQUEST_INPUT);
582+
583+
Hmac::from_engine(hmac)
584+
}
585+
569586
#[cfg(async_payments)]
570587
pub(crate) fn hmac_for_offer_paths_context(
571588
nonce: Nonce, expanded_key: &ExpandedKey,

lightning/src/onion_message/async_payments.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ const INVOICE_PERSISTED_TLV_TYPE: u64 = 65544;
2929
const HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72;
3030
const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
3131

32+
/// Used to expire the blinded paths created by the static invoice server that the async recipient
33+
/// is configured with via [`UserConfig::paths_to_static_invoice_server`].
34+
///
35+
/// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
36+
pub const DEFAULT_CONFIG_PATH_RELATIVE_EXPIRY: Duration = Duration::from_secs(30 * 24 * 60 * 60);
37+
3238
// Used to expire reply paths used in exchanging static invoice server onion messages. We expect
3339
// these onion messages to be exchanged quickly, but add some buffer for no-std users who rely on
3440
// block timestamps.

0 commit comments

Comments
 (0)