diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index ccef4480efc..590e7042dd4 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -302,7 +302,6 @@ fn create_static_invoice_builder<'a>( payment_secret, relative_expiry_secs, recipient.node.list_usable_channels(), - recipient.node.test_get_peers_for_blinded_path(), ) .unwrap() } @@ -3286,6 +3285,90 @@ fn async_payment_mpp() { claim_payment_along_route(ClaimAlongRouteArgs::new(sender, expected_route, keysend_preimage)); } +#[test] +fn fallback_to_one_hop_release_htlc_path() { + // Check that if the sender's LSP's message router fails to find a blinded path when creating a + // path for the release_held_htlc message, they will fall back to manually creating a 1-hop + // blinded path. + let chanmon_cfgs = create_chanmon_cfgs(4); + let node_cfgs = create_node_cfgs(4, &chanmon_cfgs); + + let (sender_cfg, recipient_cfg) = (often_offline_node_cfg(), often_offline_node_cfg()); + let mut sender_lsp_cfg = test_default_channel_config(); + sender_lsp_cfg.enable_htlc_hold = true; + let mut invoice_server_cfg = test_default_channel_config(); + invoice_server_cfg.accept_forwards_to_priv_channels = true; + + let node_chanmgrs = create_node_chanmgrs( + 4, + &node_cfgs, + &[Some(sender_cfg), Some(sender_lsp_cfg), Some(invoice_server_cfg), Some(recipient_cfg)], + ); + let nodes = create_network(4, &node_cfgs, &node_chanmgrs); + create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); + create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0); + // Make sure all nodes are at the same block height + let node_max_height = + nodes.iter().map(|node| node.blocks.lock().unwrap().len()).max().unwrap() as u32; + connect_blocks(&nodes[0], node_max_height - nodes[0].best_block_info().1); + connect_blocks(&nodes[1], node_max_height - nodes[1].best_block_info().1); + connect_blocks(&nodes[2], node_max_height - nodes[2].best_block_info().1); + connect_blocks(&nodes[3], node_max_height - nodes[3].best_block_info().1); + let sender = &nodes[0]; + let sender_lsp = &nodes[1]; + let invoice_server = &nodes[2]; + let recipient = &nodes[3]; + + let amt_msat = 5000; + let (static_invoice, peer_node_id, static_invoice_om) = + build_async_offer_and_init_payment(amt_msat, &nodes); + + // Force the sender_lsp's call to MessageRouter::create_blinded_paths to fail so it has to fall + // back to a 1-hop blinded path when creating the paths for its revoke_and_ack message. + sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().1 = Some(Err(())); + + let payment_hash = + lock_in_htlc_for_static_invoice(&static_invoice_om, peer_node_id, sender, sender_lsp); + + // Check that we actually had to fall back to a 1-hop path. + assert!(sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().0 > 0); + sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().1 = None; + + sender_lsp.node.process_pending_htlc_forwards(); + let (peer_id, held_htlc_om) = + extract_held_htlc_available_oms(sender, &[sender_lsp, invoice_server, recipient]) + .pop() + .unwrap(); + recipient.onion_messenger.handle_onion_message(peer_id, &held_htlc_om); + + // The release_htlc OM should go straight to the sender's LSP since they created a 1-hop blinded + // path to themselves for receiving it. + let release_htlc_om = recipient + .onion_messenger + .next_onion_message_for_peer(sender_lsp.node.get_our_node_id()) + .unwrap(); + sender_lsp + .onion_messenger + .handle_onion_message(recipient.node.get_our_node_id(), &release_htlc_om); + + sender_lsp.node.process_pending_htlc_forwards(); + let mut events = sender_lsp.node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), 1); + let ev = remove_first_msg_event_to_node(&invoice_server.node.get_our_node_id(), &mut events); + check_added_monitors!(sender_lsp, 1); + + let path: &[&Node] = &[invoice_server, recipient]; + let args = PassAlongPathArgs::new(sender_lsp, path, amt_msat, payment_hash, ev); + let claimable_ev = do_pass_along_path(args).unwrap(); + + let route: &[&[&Node]] = &[&[sender_lsp, invoice_server, recipient]]; + let keysend_preimage = extract_payment_preimage(&claimable_ev); + let (res, _) = + claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage)); + assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice))); +} + #[test] fn fail_held_htlcs_when_cfg_unset() { // Test that if we receive a held HTLC but `UserConfig::enable_htlc_hold` is unset, we will fail diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 0bdca77b366..93f77b89652 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -5441,12 +5441,10 @@ where } fn check_refresh_async_receive_offer_cache(&self, timer_tick_occurred: bool) { - let peers = self.get_peers_for_blinded_path(); let channels = self.list_usable_channels(); let entropy = &*self.entropy_source; let router = &*self.router; let refresh_res = self.flow.check_refresh_async_receive_offer_cache( - peers, channels, entropy, router, @@ -5534,10 +5532,7 @@ where ); } } else { - let reply_path = HeldHtlcReplyPath::ToUs { - payment_id, - peers: self.get_peers_for_blinded_path(), - }; + let reply_path = HeldHtlcReplyPath::ToUs { payment_id }; let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(invoice, reply_path); if enqueue_held_htlc_available_res.is_err() { @@ -8077,6 +8072,8 @@ where should_persist }); + + self.flow.set_peers(self.get_peers_for_blinded_path()); } /// Indicates that the preimage for payment_hash is unknown or the received amount is incorrect @@ -10427,16 +10424,20 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ emit_initial_channel_ready_event!(pending_events, chan); } - Ok(()) } else { try_channel_entry!(self, peer_state, Err(ChannelError::close( "Got a channel_ready message for an unfunded channel!".into())), chan_entry) } }, hash_map::Entry::Vacant(_) => { - Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id)) + return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id)) } } + core::mem::drop(peer_state_lock); + core::mem::drop(per_peer_state); + + self.flow.set_peers(self.get_peers_for_blinded_path()); + Ok(()) } fn internal_shutdown( @@ -10530,6 +10531,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ self.fail_htlc_backwards_internal(&source, &hash, &reason, receiver, None); } + self.flow.set_peers(self.get_peers_for_blinded_path()); Ok(()) } @@ -12257,9 +12259,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { /// [`Offer`]: crate::offers::offer::Offer /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> { - let builder = $self.flow.create_offer_builder( - &*$self.entropy_source, $self.get_peers_for_blinded_path() - )?; + let builder = $self.flow.create_offer_builder(&*$self.entropy_source)?; Ok(builder.into()) } @@ -12282,9 +12282,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { where ME::Target: MessageRouter, { - let builder = $self.flow.create_offer_builder_using_router( - router, &*$self.entropy_source, $self.get_peers_for_blinded_path() - )?; + let builder = $self.flow.create_offer_builder_using_router(router, &*$self.entropy_source)?; Ok(builder.into()) } @@ -12338,8 +12336,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { let entropy = &*$self.entropy_source; let builder = $self.flow.create_refund_builder( - entropy, amount_msats, absolute_expiry, - payment_id, $self.get_peers_for_blinded_path() + entropy, amount_msats, absolute_expiry, payment_id )?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self); @@ -12382,8 +12379,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { let entropy = &*$self.entropy_source; let builder = $self.flow.create_refund_builder_using_router( - router, entropy, amount_msats, absolute_expiry, - payment_id, $self.get_peers_for_blinded_path() + router, entropy, amount_msats, absolute_expiry, payment_id )?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self); @@ -12455,8 +12451,7 @@ where pub fn set_paths_to_static_invoice_server( &self, paths_to_static_invoice_server: Vec, ) -> Result<(), ()> { - let peers = self.get_peers_for_blinded_path(); - self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server, peers)?; + self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server)?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); Ok(()) @@ -12636,10 +12631,7 @@ where let invoice_request = builder.build_and_sign()?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); - self.flow.enqueue_invoice_request( - invoice_request.clone(), payment_id, nonce, - self.get_peers_for_blinded_path() - )?; + self.flow.enqueue_invoice_request(invoice_request.clone(), payment_id, nonce,)?; let retryable_invoice_request = RetryableInvoiceRequest { invoice_request: invoice_request.clone(), @@ -12694,7 +12686,7 @@ where let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; - self.flow.enqueue_invoice(invoice.clone(), refund, self.get_peers_for_blinded_path())?; + self.flow.enqueue_invoice(invoice.clone(), refund)?; Ok(invoice) }, @@ -12758,14 +12750,7 @@ where optional_params.payer_note, )?; - self.flow - .enqueue_dns_onion_message( - onion_message, - context, - dns_resolvers, - self.get_peers_for_blinded_path(), - ) - .map_err(|_| ()) + self.flow.enqueue_dns_onion_message(onion_message, context, dns_resolvers).map_err(|_| ()) } /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing @@ -12906,8 +12891,7 @@ where pub fn blinded_paths_for_async_recipient( &self, recipient_id: Vec, relative_expiry: Option, ) -> Result, ()> { - let peers = self.get_peers_for_blinded_path(); - self.flow.blinded_paths_for_async_recipient(recipient_id, relative_expiry, peers) + self.flow.blinded_paths_for_async_recipient(recipient_id, relative_expiry) } pub(super) fn duration_since_epoch(&self) -> Duration { @@ -12941,11 +12925,6 @@ where .collect::>() } - #[cfg(test)] - pub(super) fn test_get_peers_for_blinded_path(&self) -> Vec { - self.get_peers_for_blinded_path() - } - #[cfg(test)] /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. @@ -13391,6 +13370,8 @@ where for (err, counterparty_node_id) in failed_channels.drain(..) { let _ = handle_error!(self, err, counterparty_node_id); } + + self.flow.set_peers(self.get_peers_for_blinded_path()); } #[rustfmt::skip] @@ -13503,6 +13484,7 @@ where // until we have some peer connection(s) to receive onion messages over, so as a minor optimization // refresh the cache when a peer connects. self.check_refresh_async_receive_offer_cache(false); + self.flow.set_peers(self.get_peers_for_blinded_path()); res } @@ -14725,9 +14707,8 @@ where { let RetryableInvoiceRequest { invoice_request, nonce, .. } = retryable_invoice_request; - let peers = self.get_peers_for_blinded_path(); let enqueue_invreq_res = - self.flow.enqueue_invoice_request(invoice_request, payment_id, nonce, peers); + self.flow.enqueue_invoice_request(invoice_request, payment_id, nonce); if enqueue_invreq_res.is_err() { log_warn!( self.logger, @@ -14935,9 +14916,8 @@ where &self, message: OfferPathsRequest, context: AsyncPaymentsContext, responder: Option, ) -> Option<(OfferPaths, ResponseInstruction)> { - let peers = self.get_peers_for_blinded_path(); let (message, reply_path_context) = - match self.flow.handle_offer_paths_request(&message, context, peers) { + match self.flow.handle_offer_paths_request(&message, context) { Some(msg) => msg, None => return None, }; @@ -14955,7 +14935,6 @@ where message, context, responder.clone(), - self.get_peers_for_blinded_path(), self.list_usable_channels(), &*self.entropy_source, &*self.router, diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs index a6484f0076e..82e7ddda1ce 100644 --- a/lightning/src/offers/flow.rs +++ b/lightning/src/offers/flow.rs @@ -100,6 +100,7 @@ where pending_async_payments_messages: Mutex>, async_receive_offer_cache: Mutex, + peers_cache: Mutex>, #[cfg(feature = "dnssec")] pub(crate) hrn_resolver: OMNameResolver, @@ -136,6 +137,7 @@ where pending_offers_messages: Mutex::new(Vec::new()), pending_async_payments_messages: Mutex::new(Vec::new()), + peers_cache: Mutex::new(Vec::new()), #[cfg(feature = "dnssec")] hrn_resolver: OMNameResolver::new(current_timestamp, best_block.height), @@ -171,7 +173,6 @@ where /// client, or when the paths change, e.g. if the paths are set to expire at a particular time. pub fn set_paths_to_static_invoice_server( &self, paths_to_static_invoice_server: Vec, - peers: Vec, ) -> Result<(), ()> { let mut cache = self.async_receive_offer_cache.lock().unwrap(); cache.set_paths_to_static_invoice_server(paths_to_static_invoice_server.clone())?; @@ -179,7 +180,7 @@ where // We'll only fail here if no peers are connected yet for us to create reply paths to outbound // offer_paths_requests, so ignore the error. - let _ = self.check_refresh_async_offers(peers, false); + let _ = self.check_refresh_async_offers(false); Ok(()) } @@ -284,7 +285,6 @@ where /// Errors if blinded path creation fails or the provided `recipient_id` is larger than 1KiB. pub fn blinded_paths_for_async_recipient( &self, recipient_id: Vec, relative_expiry: Option, - peers: Vec, ) -> Result, ()> { if recipient_id.len() > 1024 { log_trace!(self.logger, "Async recipient ID exceeds 1024 bytes"); @@ -298,19 +298,18 @@ where recipient_id, path_absolute_expiry, }); - self.create_blinded_paths(peers, context) + self.create_blinded_paths(context) } /// Creates a collection of blinded paths by delegating to /// [`MessageRouter::create_blinded_paths`]. /// /// Errors if the `MessageRouter` errors. - fn create_blinded_paths( - &self, peers: Vec, context: MessageContext, - ) -> Result, ()> { + fn create_blinded_paths(&self, context: MessageContext) -> Result, ()> { let recipient = self.get_our_node_id(); let receive_key = self.get_receive_auth_key(); let secp_ctx = &self.secp_ctx; + let peers = self.peers_cache.lock().unwrap().clone(); self.message_router .create_blinded_paths(recipient, receive_key, context, peers, secp_ctx) @@ -429,8 +428,6 @@ pub enum HeldHtlcReplyPath { ToUs { /// The id of the payment. payment_id: PaymentId, - /// The peers to use when creating this reply path. - peers: Vec, }, /// The reply path to the [`HeldHtlcAvailable`] message should terminate at our next-hop channel /// counterparty, as they are holding our HTLC until they receive the corresponding @@ -601,13 +598,13 @@ where /// /// [`DefaultMessageRouter`]: crate::onion_message::messenger::DefaultMessageRouter pub fn create_offer_builder( - &self, entropy_source: ES, peers: Vec, + &self, entropy_source: ES, ) -> Result, Bolt12SemanticError> where ES::Target: EntropySource, { self.create_offer_builder_intern(&*entropy_source, |_, context, _| { - self.create_blinded_paths(peers, context) + self.create_blinded_paths(context) .map(|paths| paths.into_iter().take(1)) .map_err(|_| Bolt12SemanticError::MissingPaths) }) @@ -622,13 +619,14 @@ where /// /// See [`Self::create_offer_builder`] for more details on usage. pub fn create_offer_builder_using_router( - &self, router: ME, entropy_source: ES, peers: Vec, + &self, router: ME, entropy_source: ES, ) -> Result, Bolt12SemanticError> where ME::Target: MessageRouter, ES::Target: EntropySource, { let receive_key = self.get_receive_auth_key(); + let peers = self.peers_cache.lock().unwrap().clone(); self.create_offer_builder_intern(&*entropy_source, |node_id, context, secp_ctx| { router .create_blinded_paths(node_id, receive_key, context, peers, secp_ctx) @@ -732,7 +730,7 @@ where /// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value pub fn create_refund_builder( &self, entropy_source: ES, amount_msats: u64, absolute_expiry: Duration, - payment_id: PaymentId, peers: Vec, + payment_id: PaymentId, ) -> Result, Bolt12SemanticError> where ES::Target: EntropySource, @@ -740,7 +738,7 @@ where self.create_refund_builder_intern( &*entropy_source, |_, context, _| { - self.create_blinded_paths(peers, context) + self.create_blinded_paths(context) .map(|paths| paths.into_iter().take(1)) .map_err(|_| Bolt12SemanticError::MissingPaths) }, @@ -771,13 +769,14 @@ where /// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value pub fn create_refund_builder_using_router( &self, router: ME, entropy_source: ES, amount_msats: u64, absolute_expiry: Duration, - payment_id: PaymentId, peers: Vec, + payment_id: PaymentId, ) -> Result, Bolt12SemanticError> where ME::Target: MessageRouter, ES::Target: EntropySource, { let receive_key = self.get_receive_auth_key(); + let peers = self.peers_cache.lock().unwrap().clone(); self.create_refund_builder_intern( &*entropy_source, |node_id, context, secp_ctx| { @@ -817,7 +816,7 @@ where pub fn create_static_invoice_builder<'a, ES: Deref, R: Deref>( &self, router: &R, entropy_source: ES, offer: &'a Offer, offer_nonce: Nonce, payment_secret: PaymentSecret, relative_expiry_secs: u32, - usable_channels: Vec, peers: Vec, + usable_channels: Vec, ) -> Result, Bolt12SemanticError> where ES::Target: EntropySource, @@ -858,9 +857,8 @@ where path_absolute_expiry, }); - let async_receive_message_paths = self - .create_blinded_paths(peers, context) - .map_err(|()| Bolt12SemanticError::MissingPaths)?; + let async_receive_message_paths = + self.create_blinded_paths(context).map_err(|()| Bolt12SemanticError::MissingPaths)?; StaticInvoiceBuilder::for_offer_using_derived_keys( offer, @@ -1046,21 +1044,13 @@ where /// /// See [`OffersMessageFlow::create_invoice_request_builder`] for more details. /// - /// # Peers - /// - /// The user must provide a list of [`MessageForwardNode`] that will be used to generate - /// valid reply paths for the counterparty to send back the corresponding [`Bolt12Invoice`] - /// or [`InvoiceError`]. - /// /// [`supports_onion_messages`]: crate::types::features::Features::supports_onion_messages pub fn enqueue_invoice_request( &self, invoice_request: InvoiceRequest, payment_id: PaymentId, nonce: Nonce, - peers: Vec, ) -> Result<(), Bolt12SemanticError> { let context = MessageContext::Offers(OffersContext::OutboundPayment { payment_id, nonce }); - let reply_paths = self - .create_blinded_paths(peers, context) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + let reply_paths = + self.create_blinded_paths(context).map_err(|_| Bolt12SemanticError::MissingPaths)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); if !invoice_request.paths().is_empty() { @@ -1091,23 +1081,16 @@ where /// Enqueues the created [`Bolt12Invoice`] corresponding to a [`Refund`] to be sent /// to the counterparty. /// - /// # Peers - /// - /// The user must provide a list of [`MessageForwardNode`] that will be used to generate valid - /// reply paths for the counterparty to send back the corresponding [`InvoiceError`] if we fail - /// to create blinded reply paths - /// /// [`supports_onion_messages`]: crate::types::features::Features::supports_onion_messages pub fn enqueue_invoice( - &self, invoice: Bolt12Invoice, refund: &Refund, peers: Vec, + &self, invoice: Bolt12Invoice, refund: &Refund, ) -> Result<(), Bolt12SemanticError> { let payment_hash = invoice.payment_hash(); let context = MessageContext::Offers(OffersContext::InboundPayment { payment_hash }); - let reply_paths = self - .create_blinded_paths(peers, context) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + let reply_paths = + self.create_blinded_paths(context).map_err(|_| Bolt12SemanticError::MissingPaths)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); @@ -1188,16 +1171,15 @@ where matches!(reply_path_params, HeldHtlcReplyPath::ToUs { .. }); let reply_paths = match reply_path_params { - HeldHtlcReplyPath::ToUs { payment_id, peers } => { + HeldHtlcReplyPath::ToUs { payment_id } => { let context = MessageContext::AsyncPayments(AsyncPaymentsContext::OutboundPayment { payment_id, }); - self.create_blinded_paths(peers, context) - .map_err(|_| { - log_trace!(self.logger, "Failed to create blinded paths when enqueueing held_htlc_available message"); - Bolt12SemanticError::MissingPaths - })? + self.create_blinded_paths(context).map_err(|_| { + log_trace!(self.logger, "Failed to create blinded paths when enqueueing held_htlc_available message"); + Bolt12SemanticError::MissingPaths + })? }, HeldHtlcReplyPath::ToCounterparty { path } => vec![path], }; @@ -1232,9 +1214,14 @@ where where ES::Target: EntropySource, { - // In the future, we should support multi-hop paths here. let context = MessageContext::AsyncPayments(AsyncPaymentsContext::ReleaseHeldHtlc { intercept_id }); + if let Ok(mut paths) = self.create_blinded_paths(context.clone()) { + if let Some(path) = paths.pop() { + return path; + } + } + let num_dummy_hops = PADDED_PATH_LENGTH.saturating_sub(1); BlindedMessagePath::new_with_dummy_hops( &[], @@ -1248,21 +1235,12 @@ where } /// Enqueues the created [`DNSSECQuery`] to be sent to the counterparty. - /// - /// # Peers - /// - /// The user must provide a list of [`MessageForwardNode`] that will be used to generate - /// valid reply paths for the counterparty to send back the corresponding response for - /// the [`DNSSECQuery`] message. - /// - /// [`supports_onion_messages`]: crate::types::features::Features::supports_onion_messages #[cfg(feature = "dnssec")] pub fn enqueue_dns_onion_message( &self, message: DNSSECQuery, context: DNSResolverContext, dns_resolvers: Vec, - peers: Vec, ) -> Result<(), Bolt12SemanticError> { let reply_paths = self - .create_blinded_paths(peers, MessageContext::DNSResolver(context)) + .create_blinded_paths(MessageContext::DNSResolver(context)) .map_err(|_| Bolt12SemanticError::MissingPaths)?; let message_params = dns_resolvers @@ -1330,8 +1308,8 @@ where /// /// Errors if we failed to create blinded reply paths when sending an [`OfferPathsRequest`] message. pub fn check_refresh_async_receive_offer_cache( - &self, peers: Vec, usable_channels: Vec, entropy: ES, - router: R, timer_tick_occurred: bool, + &self, usable_channels: Vec, entropy: ES, router: R, + timer_tick_occurred: bool, ) -> Result<(), ()> where ES::Target: EntropySource, @@ -1345,18 +1323,16 @@ where } } - self.check_refresh_async_offers(peers.clone(), timer_tick_occurred)?; + self.check_refresh_async_offers(timer_tick_occurred)?; if timer_tick_occurred { - self.check_refresh_static_invoices(peers, usable_channels, entropy, router); + self.check_refresh_static_invoices(usable_channels, entropy, router); } Ok(()) } - fn check_refresh_async_offers( - &self, peers: Vec, timer_tick_occurred: bool, - ) -> Result<(), ()> { + fn check_refresh_async_offers(&self, timer_tick_occurred: bool) -> Result<(), ()> { let duration_since_epoch = self.duration_since_epoch(); let mut cache = self.async_receive_offer_cache.lock().unwrap(); @@ -1374,7 +1350,7 @@ where .saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY), invoice_slot: needs_new_offer_slot, }); - let reply_paths = match self.create_blinded_paths(peers, context) { + let reply_paths = match self.create_blinded_paths(context) { Ok(paths) => paths, Err(()) => { log_error!( @@ -1406,8 +1382,7 @@ where /// Enqueue onion messages that will used to request invoice refresh from the static invoice /// server, based on the offers provided by the cache. fn check_refresh_static_invoices( - &self, peers: Vec, usable_channels: Vec, entropy: ES, - router: R, + &self, usable_channels: Vec, entropy: ES, router: R, ) where ES::Target: EntropySource, R::Target: Router, @@ -1422,7 +1397,6 @@ where let (invoice, forward_invreq_path) = match self.create_static_invoice_for_server( offer, offer_nonce, - peers.clone(), usable_channels.clone(), &*entropy, &*router, @@ -1453,7 +1427,7 @@ where // Enqueue the new serve_static_invoice messages in a separate loop to avoid holding the offer // cache lock and the pending_async_payments_messages lock at the same time. for (serve_invoice_msg, serve_invoice_path, reply_path_ctx) in serve_static_invoice_msgs { - let reply_paths = match self.create_blinded_paths(peers.clone(), reply_path_ctx) { + let reply_paths = match self.create_blinded_paths(reply_path_ctx) { Ok(paths) => paths, Err(()) => continue, }; @@ -1473,7 +1447,6 @@ where /// Sends out [`OfferPaths`] onion messages in response. pub fn handle_offer_paths_request( &self, request: &OfferPathsRequest, context: AsyncPaymentsContext, - peers: Vec, ) -> Option<(OfferPaths, MessageContext)> { let duration_since_epoch = self.duration_since_epoch(); @@ -1497,7 +1470,7 @@ where invoice_slot: request.invoice_slot, }); - match self.create_blinded_paths(peers, context) { + match self.create_blinded_paths(context) { Ok(paths) => (paths, path_absolute_expiry), Err(()) => { log_error!( @@ -1535,8 +1508,7 @@ where /// fail to create blinded paths. pub fn handle_offer_paths( &self, message: OfferPaths, context: AsyncPaymentsContext, responder: Responder, - peers: Vec, usable_channels: Vec, entropy: ES, - router: R, + usable_channels: Vec, entropy: ES, router: R, ) -> Option<(ServeStaticInvoice, MessageContext)> where ES::Target: EntropySource, @@ -1588,7 +1560,6 @@ where let (invoice, forward_invoice_request_path) = match self.create_static_invoice_for_server( &offer, offer_nonce, - peers, usable_channels, &*entropy, router, @@ -1626,8 +1597,8 @@ where /// Creates a [`StaticInvoice`] and a blinded path for the server to forward invoice requests from /// payers to our node. fn create_static_invoice_for_server( - &self, offer: &Offer, offer_nonce: Nonce, peers: Vec, - usable_channels: Vec, entropy: ES, router: R, + &self, offer: &Offer, offer_nonce: Nonce, usable_channels: Vec, + entropy: ES, router: R, ) -> Result<(StaticInvoice, BlindedMessagePath), ()> where ES::Target: EntropySource, @@ -1663,14 +1634,13 @@ where payment_secret, offer_relative_expiry, usable_channels, - peers.clone(), ) .and_then(|builder| builder.build_and_sign(secp_ctx)) .map_err(|_| ())?; let context = MessageContext::Offers(OffersContext::InvoiceRequest { nonce: offer_nonce }); let forward_invoice_request_path = self - .create_blinded_paths(peers, context) + .create_blinded_paths(context) .and_then(|paths| paths.into_iter().next().ok_or(()))?; Ok((invoice, forward_invoice_request_path)) @@ -1739,4 +1709,15 @@ where pub fn writeable_async_receive_offer_cache(&self) -> Vec { self.async_receive_offer_cache.encode() } + + /// Provides a set of connected peers of this node that can be used in [`BlindedMessagePath`]s + /// created by the [`OffersMessageFlow`]. + /// + /// All provided peers MUST advertise support for onion messages in their [`InitFeatures`]. + /// + /// [`InitFeatures`]: crate::types::features::InitFeatures + pub fn set_peers(&self, mut peers: Vec) { + let mut peers_cache = self.peers_cache.lock().unwrap(); + core::mem::swap(&mut *peers_cache, &mut peers); + } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 769c2a3ed3e..51f56eae660 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -334,6 +334,9 @@ pub enum TestMessageRouterInternal<'a> { pub struct TestMessageRouter<'a> { pub inner: TestMessageRouterInternal<'a>, pub peers_override: Mutex>, + // An override result for Self::create_blinded_paths, plus a tracker for how many times the + // overridden result has been returned. + pub create_blinded_paths_res_override: Mutex<(u8, Option, ()>>)>, } impl<'a> TestMessageRouter<'a> { @@ -346,6 +349,7 @@ impl<'a> TestMessageRouter<'a> { entropy_source, )), peers_override: Mutex::new(Vec::new()), + create_blinded_paths_res_override: Mutex::new((0, None)), } } @@ -358,6 +362,7 @@ impl<'a> TestMessageRouter<'a> { entropy_source, )), peers_override: Mutex::new(Vec::new()), + create_blinded_paths_res_override: Mutex::new((0, None)), } } } @@ -385,6 +390,15 @@ impl<'a> MessageRouter for TestMessageRouter<'a> { &self, recipient: PublicKey, local_node_receive_key: ReceiveAuthKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { + { + let mut res_override = self.create_blinded_paths_res_override.lock().unwrap(); + if let Some(res) = &res_override.1 { + let res = res.clone(); + res_override.0 += 1; + return res; + } + } + let mut peers = peers; { let peers_override = self.peers_override.lock().unwrap();