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::parse::Bolt12SemanticError;
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::ecdsa::WriteableEcdsaChannelSigner;
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
@ -7483,32 +7483,43 @@ where
///
/// # Privacy
///
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
/// the node must be announced. Otherwise, there is no way to find a path to the introduction
/// node in order to send the [`InvoiceRequest`].
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer.
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
/// 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
///
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
/// 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.
///
/// [`Offer`]: crate::offers::offer::Offer
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
pub fn create_offer_builder(
&self, description: String
) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
let node_id = self.get_our_node_id();
let expanded_key = &self.inbound_payment_key;
let entropy = &*self.entropy_source;
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)
.path(path)
.path(path);
Ok(builder)
}
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
@ -7533,10 +7544,13 @@ where
///
/// # Privacy
///
/// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
/// the introduction node and a derived payer id for payer privacy. As such, currently, the
/// node must be announced. Otherwise, there is no way to find a path to the introduction node
/// in order to send the [`Bolt12Invoice`].
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
/// 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
///
@ -7545,8 +7559,10 @@ where
///
/// # Errors
///
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
/// or if `amount_msats` is invalid.
/// Errors if:
/// - 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.
///
@ -7561,8 +7577,8 @@ where
let expanded_key = &self.inbound_payment_key;
let entropy = &*self.entropy_source;
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(
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
)?
@ -7620,8 +7636,11 @@ where
///
/// # Errors
///
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
/// or if the provided parameters are invalid for the offer.
/// Errors if:
/// - 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::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
@ -7654,9 +7673,8 @@ where
None => builder,
Some(payer_note) => builder.payer_note(payer_note),
};
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);
self.pending_outbound_payments
@ -7732,7 +7750,8 @@ where
payment_paths, payment_hash, created_at, expanded_key, entropy
)?;
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();
if refund.paths().is_empty() {
@ -7859,12 +7878,23 @@ where
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
/// node.
fn create_one_hop_blinded_path(&self) -> BlindedPath {
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
///
/// 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 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