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.
This commit is contained in:
Jeffrey Czyz 2023-03-03 09:38:45 -06:00
parent dcd8d58346
commit 606304aa32
No known key found for this signature in database
GPG Key ID: 3A4E08275D5E96D2

View File

@ -65,7 +65,7 @@ use crate::offers::merkle::SignError;
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder}; use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
use crate::offers::parse::Bolt12SemanticError; use crate::offers::parse::Bolt12SemanticError;
use crate::offers::refund::{Refund, RefundBuilder}; use crate::offers::refund::{Refund, RefundBuilder};
use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message}; use crate::onion_message::{Destination, MessageRouter, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider}; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider};
use crate::sign::ecdsa::WriteableEcdsaChannelSigner; use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
@ -7483,32 +7483,43 @@ where
/// ///
/// # Privacy /// # Privacy
/// ///
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer.
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently, /// However, if one is not found, uses a one-hop [`BlindedPath`] with
/// the node must be announced. Otherwise, there is no way to find a path to the introduction /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
/// node in order to send the [`InvoiceRequest`]. /// the node must be announced, otherwise, there is no way to find a path to the introduction in
/// order to send the [`InvoiceRequest`].
///
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
/// ///
/// # Limitations /// # Limitations
/// ///
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s /// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
/// reply path. /// reply path.
/// ///
/// # Errors
///
/// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer.
///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics. /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
/// ///
/// [`Offer`]: crate::offers::offer::Offer /// [`Offer`]: crate::offers::offer::Offer
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
pub fn create_offer_builder( pub fn create_offer_builder(
&self, description: String &self, description: String
) -> OfferBuilder<DerivedMetadata, secp256k1::All> { ) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
let node_id = self.get_our_node_id(); let node_id = self.get_our_node_id();
let expanded_key = &self.inbound_payment_key; let expanded_key = &self.inbound_payment_key;
let entropy = &*self.entropy_source; let entropy = &*self.entropy_source;
let secp_ctx = &self.secp_ctx; let secp_ctx = &self.secp_ctx;
let path = self.create_one_hop_blinded_path();
OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx) let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
let builder = OfferBuilder::deriving_signing_pubkey(
description, node_id, expanded_key, entropy, secp_ctx
)
.chain_hash(self.chain_hash) .chain_hash(self.chain_hash)
.path(path) .path(path);
Ok(builder)
} }
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
@ -7533,10 +7544,13 @@ where
/// ///
/// # Privacy /// # Privacy
/// ///
/// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
/// the introduction node and a derived payer id for payer privacy. As such, currently, the /// However, if one is not found, uses a one-hop [`BlindedPath`] with
/// node must be announced. Otherwise, there is no way to find a path to the introduction node /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
/// in order to send the [`Bolt12Invoice`]. /// the node must be announced, otherwise, there is no way to find a path to the introduction in
/// order to send the [`Bolt12Invoice`].
///
/// Also, uses a derived payer id in the refund for payer privacy.
/// ///
/// # Limitations /// # Limitations
/// ///
@ -7545,8 +7559,10 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link /// Errors if:
/// or if `amount_msats` is invalid. /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
/// - `amount_msats` is invalid, or
/// - the parameterized [`Router`] is unable to create a blinded path for the refund.
/// ///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics. /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
/// ///
@ -7561,8 +7577,8 @@ where
let expanded_key = &self.inbound_payment_key; let expanded_key = &self.inbound_payment_key;
let entropy = &*self.entropy_source; let entropy = &*self.entropy_source;
let secp_ctx = &self.secp_ctx; let secp_ctx = &self.secp_ctx;
let path = self.create_one_hop_blinded_path();
let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
let builder = RefundBuilder::deriving_payer_id( let builder = RefundBuilder::deriving_payer_id(
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
)? )?
@ -7620,8 +7636,11 @@ where
/// ///
/// # Errors /// # Errors
/// ///
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link /// Errors if:
/// or if the provided parameters are invalid for the offer. /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
/// - the provided parameters are invalid for the offer,
/// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice
/// request.
/// ///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
@ -7654,9 +7673,8 @@ where
None => builder, None => builder,
Some(payer_note) => builder.payer_note(payer_note), Some(payer_note) => builder.payer_note(payer_note),
}; };
let invoice_request = builder.build_and_sign()?; let invoice_request = builder.build_and_sign()?;
let reply_path = self.create_one_hop_blinded_path(); let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
let expiration = StaleExpiration::TimerTicks(1); let expiration = StaleExpiration::TimerTicks(1);
self.pending_outbound_payments self.pending_outbound_payments
@ -7732,7 +7750,8 @@ where
payment_paths, payment_hash, created_at, expanded_key, entropy payment_paths, payment_hash, created_at, expanded_key, entropy
)?; )?;
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
let reply_path = self.create_one_hop_blinded_path(); let reply_path = self.create_blinded_path()
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
if refund.paths().is_empty() { if refund.paths().is_empty() {
@ -7859,12 +7878,23 @@ where
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key) inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
} }
/// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
/// node. ///
fn create_one_hop_blinded_path(&self) -> BlindedPath { /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
let recipient = self.get_our_node_id();
let entropy_source = self.entropy_source.deref(); let entropy_source = self.entropy_source.deref();
let secp_ctx = &self.secp_ctx; let secp_ctx = &self.secp_ctx;
BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
let peers = self.per_peer_state.read().unwrap()
.iter()
.filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
.map(|(node_id, _)| *node_id)
.collect::<Vec<_>>();
self.router
.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
.and_then(|paths| paths.into_iter().next().ok_or(()))
} }
/// Creates a one-hop blinded payment path with [`ChannelManager::get_our_node_id`] as the /// Creates a one-hop blinded payment path with [`ChannelManager::get_our_node_id`] as the