From 6904786ed4edd1fe5d85a5b886aa4be86052f338 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 15 Apr 2024 16:25:03 +0530 Subject: [PATCH] Introduce ResponseInstruction::WithReplyPath variant. And expand handle_onion_message_response return Type 1. Introduce a new function in OnionMessenger to create blinded paths. 2. Use it in handle_onion_message_response to create a reply_path for the right variant and use it in onion_message. 3. Expand the return type of handle_onion_message_response to handle three cases: 1. Ok(None) in case of no response to be sent. 2. Ok(Some(SendSuccess) and Err(SendError) in case of successful and unsuccessful queueing up of response messages respectively. This allows the user to get access to the Success/Failure status of the sending of response and handle it accordingly. --- .../src/onion_message/functional_tests.rs | 8 +- lightning/src/onion_message/messenger.rs | 92 +++++++++++++++---- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index a777dc419..03356b434 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -19,7 +19,7 @@ use crate::routing::test_utils::{add_channel, add_or_update_node}; use crate::sign::{NodeSigner, Recipient}; use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer}; use crate::util::test_utils; -use super::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, OnionMessagePath, OnionMessenger, PendingOnionMessage, Responder, ResponseInstruction, SendError}; +use super::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, OnionMessagePath, OnionMessenger, PendingOnionMessage, Responder, ResponseInstruction, SendError, SendSuccess}; use super::offers::{OffersMessage, OffersMessageHandler}; use super::packet::{OnionMessageContents, Packet}; @@ -397,7 +397,11 @@ fn async_response_over_one_blinded_hop() { let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, responder); // 6. Simulate Alice asynchronously responding back to Bob with a response. - nodes[0].messenger.handle_onion_message_response(response_instruction); + assert_eq!( + nodes[0].messenger.handle_onion_message_response(response_instruction), + Ok(Some(SendSuccess::Buffered)), + ); + bob.custom_message_handler.expect_message(TestCustomMessage::Response); pass_along_path(&nodes); diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 8ab92ba64..aa5f65894 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -271,7 +271,9 @@ impl Responder { } } - /// Creates the appropriate [`ResponseInstruction`] for a given response. + /// Creates a [`ResponseInstruction::WithoutReplyPath`] for a given response. + /// + /// Use when the recipient doesn't need to send back a reply to us. pub fn respond(self, response: T) -> ResponseInstruction { ResponseInstruction::WithoutReplyPath(OnionMessageResponse { message: response, @@ -279,6 +281,17 @@ impl Responder { path_id: self.path_id, }) } + + /// Creates a [`ResponseInstruction::WithReplyPath`] for a given response. + /// + /// Use when the recipient needs to send back a reply to us. + pub fn respond_with_reply_path(self, response: T) -> ResponseInstruction { + ResponseInstruction::WithReplyPath(OnionMessageResponse { + message: response, + reply_path: self.reply_path, + path_id: self.path_id, + }) + } } /// This struct contains the information needed to reply to a received message. @@ -290,6 +303,9 @@ pub struct OnionMessageResponse { /// `ResponseInstruction` represents instructions for responding to received messages. pub enum ResponseInstruction { + /// Indicates that a response should be sent including a reply path for + /// the recipient to respond back. + WithReplyPath(OnionMessageResponse), /// Indicates that a response should be sent without including a reply path /// for the recipient to respond back. WithoutReplyPath(OnionMessageResponse), @@ -564,7 +580,11 @@ pub enum SendError { TooFewBlindedHops, /// The first hop is not a peer and doesn't have a known [`SocketAddress`]. InvalidFirstHop(PublicKey), - /// A path from the sender to the destination could not be found by the [`MessageRouter`]. + /// Indicates that a path could not be found by the [`MessageRouter`]. + /// + /// This occurs when either: + /// - No path from the sender to the destination was found to send the onion message + /// - No reply path to the sender could be created when responding to an onion message PathNotFound, /// Onion message contents must have a TLV type >= 64. InvalidMessage, @@ -981,6 +1001,27 @@ where .map_err(|_| SendError::PathNotFound) } + fn create_blinded_path(&self) -> Result { + let recipient = self.node_signer + .get_node_id(Recipient::Node) + .map_err(|_| SendError::GetNodeIdFailed)?; + let secp_ctx = &self.secp_ctx; + + let peers = self.message_recipients.lock().unwrap() + .iter() + .filter(|(_, peer)| matches!(peer, OnionMessageRecipient::ConnectedPeer(_))) + .map(|(node_id, _ )| ForwardNode { + node_id: *node_id, + short_channel_id: None, + }) + .collect::>(); + + self.message_router + .create_blinded_paths(recipient, peers, secp_ctx) + .and_then(|paths| paths.into_iter().next().ok_or(())) + .map_err(|_| SendError::PathNotFound) + } + fn enqueue_onion_message( &self, path: OnionMessagePath, contents: T, reply_path: Option, log_suffix: fmt::Arguments @@ -1064,18 +1105,37 @@ where /// the response for delivery. pub fn handle_onion_message_response( &self, response: ResponseInstruction - ) { - if let ResponseInstruction::WithoutReplyPath(response) = response { - let message_type = response.message.msg_type(); - let _ = self.find_path_and_enqueue_onion_message( - response.message, Destination::BlindedPath(response.reply_path), None, - format_args!( - "when responding with {} to an onion message with path_id {:02x?}", - message_type, - response.path_id - ) - ); - } + ) -> Result, SendError> { + let (response, create_reply_path) = match response { + ResponseInstruction::WithReplyPath(response) => (response, true), + ResponseInstruction::WithoutReplyPath(response) => (response, false), + ResponseInstruction::NoResponse => return Ok(None), + }; + + let message_type = response.message.msg_type(); + let reply_path = if create_reply_path { + match self.create_blinded_path() { + Ok(reply_path) => Some(reply_path), + Err(err) => { + log_trace!( + self.logger, + "Failed to create reply path when responding with {} to an onion message \ + with path_id {:02x?}: {:?}", + message_type, response.path_id, err + ); + return Err(err); + } + } + } else { None }; + + self.find_path_and_enqueue_onion_message( + response.message, Destination::BlindedPath(response.reply_path), reply_path, + format_args!( + "when responding with {} to an onion message with path_id {:02x?}", + message_type, + response.path_id + ) + ).map(|result| Some(result)) } #[cfg(test)] @@ -1181,14 +1241,14 @@ where |reply_path| Responder::new(reply_path, path_id) ); let response_instructions = self.offers_handler.handle_message(msg, responder); - self.handle_onion_message_response(response_instructions); + let _ = self.handle_onion_message_response(response_instructions); }, ParsedOnionMessageContents::Custom(msg) => { let responder = reply_path.map( |reply_path| Responder::new(reply_path, path_id) ); let response_instructions = self.custom_handler.handle_custom_message(msg, responder); - self.handle_onion_message_response(response_instructions); + let _ = self.handle_onion_message_response(response_instructions); }, } }, -- 2.39.5