Skip to content

Commit 606304a

Browse files
committed
Multi-hop blinded paths in ChannelManager
When constructing blinded paths for Offer and Refund, delegate to MessageRouter::create_blinded_paths which may produce multi-hop blinded paths. Fallback to one-hop blinded paths if the MessageRouter fails or returns no paths. Likewise, do the same for InvoiceRequest and Bolt12Invoice reply paths.
1 parent dcd8d58 commit 606304a

File tree

1 file changed

+55
-25
lines changed

1 file changed

+55
-25
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ use crate::offers::merkle::SignError;
6565
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
6666
use crate::offers::parse::Bolt12SemanticError;
6767
use crate::offers::refund::{Refund, RefundBuilder};
68-
use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
68+
use crate::onion_message::{Destination, MessageRouter, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
6969
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider};
7070
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
7171
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
@@ -7483,32 +7483,43 @@ where
74837483
///
74847484
/// # Privacy
74857485
///
7486-
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
7487-
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
7488-
/// the node must be announced. Otherwise, there is no way to find a path to the introduction
7489-
/// node in order to send the [`InvoiceRequest`].
7486+
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer.
7487+
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
7488+
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
7489+
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
7490+
/// order to send the [`InvoiceRequest`].
7491+
///
7492+
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
74907493
///
74917494
/// # Limitations
74927495
///
74937496
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
74947497
/// reply path.
74957498
///
7499+
/// # Errors
7500+
///
7501+
/// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer.
7502+
///
74967503
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
74977504
///
74987505
/// [`Offer`]: crate::offers::offer::Offer
74997506
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
75007507
pub fn create_offer_builder(
75017508
&self, description: String
7502-
) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
7509+
) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
75037510
let node_id = self.get_our_node_id();
75047511
let expanded_key = &self.inbound_payment_key;
75057512
let entropy = &*self.entropy_source;
75067513
let secp_ctx = &self.secp_ctx;
7507-
let path = self.create_one_hop_blinded_path();
75087514

7509-
OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx)
7515+
let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
7516+
let builder = OfferBuilder::deriving_signing_pubkey(
7517+
description, node_id, expanded_key, entropy, secp_ctx
7518+
)
75107519
.chain_hash(self.chain_hash)
7511-
.path(path)
7520+
.path(path);
7521+
7522+
Ok(builder)
75127523
}
75137524

75147525
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
@@ -7533,10 +7544,13 @@ where
75337544
///
75347545
/// # Privacy
75357546
///
7536-
/// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
7537-
/// the introduction node and a derived payer id for payer privacy. As such, currently, the
7538-
/// node must be announced. Otherwise, there is no way to find a path to the introduction node
7539-
/// in order to send the [`Bolt12Invoice`].
7547+
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
7548+
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
7549+
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
7550+
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
7551+
/// order to send the [`Bolt12Invoice`].
7552+
///
7553+
/// Also, uses a derived payer id in the refund for payer privacy.
75407554
///
75417555
/// # Limitations
75427556
///
@@ -7545,8 +7559,10 @@ where
75457559
///
75467560
/// # Errors
75477561
///
7548-
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
7549-
/// or if `amount_msats` is invalid.
7562+
/// Errors if:
7563+
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
7564+
/// - `amount_msats` is invalid, or
7565+
/// - the parameterized [`Router`] is unable to create a blinded path for the refund.
75507566
///
75517567
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
75527568
///
@@ -7561,8 +7577,8 @@ where
75617577
let expanded_key = &self.inbound_payment_key;
75627578
let entropy = &*self.entropy_source;
75637579
let secp_ctx = &self.secp_ctx;
7564-
let path = self.create_one_hop_blinded_path();
75657580

7581+
let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
75667582
let builder = RefundBuilder::deriving_payer_id(
75677583
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
75687584
)?
@@ -7620,8 +7636,11 @@ where
76207636
///
76217637
/// # Errors
76227638
///
7623-
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
7624-
/// or if the provided parameters are invalid for the offer.
7639+
/// Errors if:
7640+
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
7641+
/// - the provided parameters are invalid for the offer,
7642+
/// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice
7643+
/// request.
76257644
///
76267645
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
76277646
/// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
@@ -7654,9 +7673,8 @@ where
76547673
None => builder,
76557674
Some(payer_note) => builder.payer_note(payer_note),
76567675
};
7657-
76587676
let invoice_request = builder.build_and_sign()?;
7659-
let reply_path = self.create_one_hop_blinded_path();
7677+
let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
76607678

76617679
let expiration = StaleExpiration::TimerTicks(1);
76627680
self.pending_outbound_payments
@@ -7732,7 +7750,8 @@ where
77327750
payment_paths, payment_hash, created_at, expanded_key, entropy
77337751
)?;
77347752
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
7735-
let reply_path = self.create_one_hop_blinded_path();
7753+
let reply_path = self.create_blinded_path()
7754+
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
77367755

77377756
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
77387757
if refund.paths().is_empty() {
@@ -7859,12 +7878,23 @@ where
78597878
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
78607879
}
78617880

7862-
/// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
7863-
/// node.
7864-
fn create_one_hop_blinded_path(&self) -> BlindedPath {
7881+
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
7882+
///
7883+
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
7884+
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
7885+
let recipient = self.get_our_node_id();
78657886
let entropy_source = self.entropy_source.deref();
78667887
let secp_ctx = &self.secp_ctx;
7867-
BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
7888+
7889+
let peers = self.per_peer_state.read().unwrap()
7890+
.iter()
7891+
.filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
7892+
.map(|(node_id, _)| *node_id)
7893+
.collect::<Vec<_>>();
7894+
7895+
self.router
7896+
.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
7897+
.and_then(|paths| paths.into_iter().next().ok_or(()))
78687898
}
78697899

78707900
/// Creates a one-hop blinded payment path with [`ChannelManager::get_our_node_id`] as the

0 commit comments

Comments
 (0)