diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index a44743470b0..d096f306b3b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -1555,6 +1555,9 @@ const MAX_UNFUNDED_CHANNEL_PEERS: usize = 50; /// many peers we reject new (inbound) connections. const MAX_NO_CHANNEL_PEERS: usize = 250; +/// The maximum number of blinded payment paths to use in BOLT 12 invoices. +const MAX_BLINDED_PAYMENT_PATHS: usize = 3; + /// Information needed for constructing an invoice route hint for this channel. #[derive(Clone, Debug, PartialEq)] pub struct CounterpartyForwardingInfo { @@ -7652,9 +7655,9 @@ where match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) { Ok((payment_hash, payment_secret)) => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; + let payment_paths = self.create_blinded_payment_paths( + amount_msats, payment_secret, MAX_BLINDED_PAYMENT_PATHS + ); #[cfg(not(feature = "no-std"))] let builder = refund.respond_using_derived_keys( payment_paths, payment_hash, expanded_key, entropy @@ -7831,6 +7834,18 @@ where self.router.create_blinded_paths(recipient, peers, count, entropy_source, secp_ctx) } + /// Creates `count` multi-hop blinded payment paths for the given `amount_msats` by delegating + /// to [`Router::create_blinded_payment_paths`]. If the router returns an error or no paths, + /// creates a one-hop blinded payment path instead. + fn create_blinded_payment_paths( + &self, amount_msats: u64, payment_secret: PaymentSecret, count: usize + ) -> Vec<(BlindedPayInfo, BlindedPath)> { + self.create_multi_hop_blinded_payment_paths(amount_msats, payment_secret, count) + .ok() + .and_then(|paths| (!paths.is_empty()).then(|| paths)) + .unwrap_or_else(|| vec![self.create_one_hop_blinded_payment_path(payment_secret)]) + } + /// Creates a one-hop blinded payment path with [`ChannelManager::get_our_node_id`] as the /// introduction node. fn create_one_hop_blinded_payment_path( @@ -7854,6 +7869,34 @@ where ).unwrap() } + /// Creates `count` multi-hop blinded payment paths for the given `amount_msats` by delegating + /// to [`Router::create_blinded_payment_paths`]. + /// + /// May return fewer paths if not enough peers [`support route blinding`] or whose channels + /// don't have enough inbound liquidity. + /// + /// [`support route blinding`]: crate::ln::features::InitFeatures::supports_route_blinding + fn create_multi_hop_blinded_payment_paths( + &self, amount_msats: u64, payment_secret: PaymentSecret, count: usize + ) -> Result, ()> { + let entropy_source = self.entropy_source.deref(); + let secp_ctx = &self.secp_ctx; + + let first_hops = self.list_usable_channels(); + let payee_node_id = self.get_our_node_id(); + let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS; + let payee_tlvs = ReceiveTlvs { + payment_secret, + payment_constraints: PaymentConstraints { + max_cltv_expiry, + htlc_minimum_msat: 1, + }, + }; + self.router.create_blinded_payment_paths( + payee_node_id, first_hops, payee_tlvs, amount_msats, count, entropy_source, secp_ctx + ) + } + /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids /// are used when constructing the phantom invoice's route hints. /// @@ -9091,7 +9134,7 @@ where let amount_msats = match InvoiceBuilder::::amount_msats( &invoice_request ) { - Ok(amount_msats) => Some(amount_msats), + Ok(amount_msats) => amount_msats, Err(error) => return Some(OffersMessage::InvoiceError(error.into())), }; let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) { @@ -9103,11 +9146,11 @@ where }; let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; - match self.create_inbound_payment(amount_msats, relative_expiry, None) { + match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) { Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; + let payment_paths = self.create_blinded_payment_paths( + amount_msats, payment_secret, MAX_BLINDED_PAYMENT_PATHS + ); #[cfg(not(feature = "no-std"))] let builder = invoice_request.respond_using_derived_keys( payment_paths, payment_hash @@ -9126,9 +9169,9 @@ where } }, Ok((payment_hash, payment_secret)) => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; + let payment_paths = self.create_blinded_payment_paths( + amount_msats, payment_secret, MAX_BLINDED_PAYMENT_PATHS + ); #[cfg(not(feature = "no-std"))] let builder = invoice_request.respond_with(payment_paths, payment_hash); #[cfg(feature = "no-std")]