]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Expand `create_blinded_path` Functionality for Enhanced Path Diversification
authorshaavan <shaavan.github@gmail.com>
Mon, 10 Jun 2024 13:45:19 +0000 (19:15 +0530)
committershaavan <shaavan.github@gmail.com>
Thu, 11 Jul 2024 13:42:34 +0000 (19:12 +0530)
- Previously, the `create_blinded_path` function was limited to
  returning a single `BlindedPath`, which restricted the usage of
  `blinded_paths`.
- This commit extends the `create_blinded_path` function to return
  the entire blinded path vector generated by the `MessageRouter`'s
  `create_blinded_paths`.
- The updated functionality is integrated across the codebase, enabling
  the sending of Offers Response messages, such as `InvoiceRequest`
  (in `pay_for_offer`) and `Invoice` (in `request_refund_payment`),
  utilizing multiple reply paths.

lightning/src/ln/channelmanager.rs

index 89f967c4f700f6935870ea4d7c33a800d439a8dc..955a155613627ff8c65221781c05f366e53ea688 100644 (file)
@@ -8621,8 +8621,10 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
                let entropy = &*$self.entropy_source;
                let secp_ctx = &$self.secp_ctx;
 
-               let path = $self.create_blinded_path_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry)
+               let path = $self.create_blinded_paths_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry)
+                       .and_then(|paths| paths.into_iter().next().ok_or(()))
                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
+
                let builder = OfferBuilder::deriving_signing_pubkey(
                        node_id, expanded_key, entropy, secp_ctx
                )
@@ -8694,8 +8696,10 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
                let secp_ctx = &$self.secp_ctx;
 
                let context = OffersContext::OutboundPayment { payment_id };
-               let path = $self.create_blinded_path_using_absolute_expiry(context, Some(absolute_expiry))
+               let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
+                       .and_then(|paths| paths.into_iter().next().ok_or(()))
                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
+
                let builder = RefundBuilder::deriving_payer_id(
                        node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
                )?
@@ -8716,6 +8720,13 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
        }
 } }
 
+/// Defines the maximum number of [`OffersMessage`] including different reply paths to be sent
+/// along different paths.
+/// Sending multiple requests increases the chances of successful delivery in case some
+/// paths are unavailable. However, only one invoice for a given [`PaymentId`] will be paid,
+/// even if multiple invoices are received.
+const OFFERS_MESSAGE_REQUEST_LIMIT: usize = 10;
+
 impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> ChannelManager<M, T, ES, NS, SP, F, R, L>
 where
        M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -8819,7 +8830,7 @@ where
                let invoice_request = builder.build_and_sign()?;
 
                let context = OffersContext::OutboundPayment { payment_id };
-               let reply_path = self.create_blinded_path(context).map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               let reply_paths = self.create_blinded_paths(context).map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
@@ -8832,25 +8843,27 @@ where
 
                let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
                if !offer.paths().is_empty() {
-                       // Send as many invoice requests as there are paths in the offer (with an upper bound).
-                       // Using only one path could result in a failure if the path no longer exists. But only
-                       // one invoice for a given payment id will be paid, even if more than one is received.
-                       const REQUEST_LIMIT: usize = 10;
-                       for path in offer.paths().into_iter().take(REQUEST_LIMIT) {
+                       reply_paths
+                               .iter()
+                               .flat_map(|reply_path| offer.paths().iter().map(move |path| (path, reply_path)))
+                               .take(OFFERS_MESSAGE_REQUEST_LIMIT)
+                               .for_each(|(path, reply_path)| {
+                                       let message = new_pending_onion_message(
+                                               OffersMessage::InvoiceRequest(invoice_request.clone()),
+                                               Destination::BlindedPath(path.clone()),
+                                               Some(reply_path.clone()),
+                                       );
+                                       pending_offers_messages.push(message);
+                               });
+               } else if let Some(signing_pubkey) = offer.signing_pubkey() {
+                       for reply_path in reply_paths {
                                let message = new_pending_onion_message(
                                        OffersMessage::InvoiceRequest(invoice_request.clone()),
-                                       Destination::BlindedPath(path.clone()),
-                                       Some(reply_path.clone()),
+                                       Destination::Node(signing_pubkey),
+                                       Some(reply_path),
                                );
                                pending_offers_messages.push(message);
                        }
-               } else if let Some(signing_pubkey) = offer.signing_pubkey() {
-                       let message = new_pending_onion_message(
-                               OffersMessage::InvoiceRequest(invoice_request),
-                               Destination::Node(signing_pubkey),
-                               Some(reply_path),
-                       );
-                       pending_offers_messages.push(message);
                } else {
                        debug_assert!(false);
                        return Err(Bolt12SemanticError::MissingSigningPubkey);
@@ -8919,26 +8932,32 @@ where
                                )?;
                                let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
                                let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
-                               let reply_path = self.create_blinded_path(OffersContext::Unknown {})
+                               let reply_paths = self.create_blinded_paths(OffersContext::Unknown {})
                                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
                                let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
                                if refund.paths().is_empty() {
-                                       let message = new_pending_onion_message(
-                                               OffersMessage::Invoice(invoice.clone()),
-                                               Destination::Node(refund.payer_id()),
-                                               Some(reply_path),
-                                       );
-                                       pending_offers_messages.push(message);
-                               } else {
-                                       for path in refund.paths() {
+                                       for reply_path in reply_paths {
                                                let message = new_pending_onion_message(
                                                        OffersMessage::Invoice(invoice.clone()),
-                                                       Destination::BlindedPath(path.clone()),
-                                                       Some(reply_path.clone()),
+                                                       Destination::Node(refund.payer_id()),
+                                                       Some(reply_path),
                                                );
                                                pending_offers_messages.push(message);
                                        }
+                               } else {
+                                       reply_paths
+                                               .iter()
+                                               .flat_map(|reply_path| refund.paths().iter().map(move |path| (path, reply_path)))
+                                               .take(OFFERS_MESSAGE_REQUEST_LIMIT)
+                                               .for_each(|(path, reply_path)| {
+                                                       let message = new_pending_onion_message(
+                                                               OffersMessage::Invoice(invoice.clone()),
+                                                               Destination::BlindedPath(path.clone()),
+                                                               Some(reply_path.clone()),
+                                                       );
+                                                       pending_offers_messages.push(message);
+                                               });
                                }
 
                                Ok(invoice)
@@ -9045,22 +9064,22 @@ where
                inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
        }
 
-       /// Creates a blinded path by delegating to [`MessageRouter`] based on the path's intended
-       /// lifetime.
+       /// Creates a collection of blinded paths by delegating to [`MessageRouter`] based on
+       /// the path's intended lifetime.
        ///
        /// Whether or not the path is compact depends on whether the path is short-lived or long-lived,
        /// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch. See
        /// [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`].
-       fn create_blinded_path_using_absolute_expiry(
+       fn create_blinded_paths_using_absolute_expiry(
                &self, context: OffersContext, absolute_expiry: Option<Duration>,
-       ) -> Result<BlindedPath, ()> {
+       ) -> Result<Vec<BlindedPath>, ()> {
                let now = self.duration_since_epoch();
                let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY);
 
                if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry {
-                       self.create_compact_blinded_path(context)
+                       self.create_compact_blinded_paths(context)
                } else {
-                       self.create_blinded_path(context)
+                       self.create_blinded_paths(context)
                }
        }
 
@@ -9077,10 +9096,11 @@ where
                now
        }
 
-       /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
+       /// Creates a collection of blinded paths by delegating to
+       /// [`MessageRouter::create_blinded_paths`].
        ///
-       /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
-       fn create_blinded_path(&self, context: OffersContext) -> Result<BlindedPath, ()> {
+       /// Errors if the `MessageRouter` errors.
+       fn create_blinded_paths(&self, context: OffersContext) -> Result<Vec<BlindedPath>, ()> {
                let recipient = self.get_our_node_id();
                let secp_ctx = &self.secp_ctx;
 
@@ -9094,13 +9114,14 @@ where
 
                self.router
                        .create_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx)
-                       .and_then(|paths| paths.into_iter().next().ok_or(()))
+                       .and_then(|paths| (!paths.is_empty()).then(|| paths).ok_or(()))
        }
 
-       /// Creates a blinded path by delegating to [`MessageRouter::create_compact_blinded_paths`].
+       /// Creates a collection of blinded paths by delegating to
+       /// [`MessageRouter::create_compact_blinded_paths`].
        ///
-       /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
-       fn create_compact_blinded_path(&self, context: OffersContext) -> Result<BlindedPath, ()> {
+       /// Errors if the `MessageRouter` errors.
+       fn create_compact_blinded_paths(&self, context: OffersContext) -> Result<Vec<BlindedPath>, ()> {
                let recipient = self.get_our_node_id();
                let secp_ctx = &self.secp_ctx;
 
@@ -9121,7 +9142,7 @@ where
 
                self.router
                        .create_compact_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx)
-                       .and_then(|paths| paths.into_iter().next().ok_or(()))
+                       .and_then(|paths| (!paths.is_empty()).then(|| paths).ok_or(()))
        }
 
        /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to