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};
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);
}
}
- /// 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<T: OnionMessageContents>(self, response: T) -> ResponseInstruction<T> {
ResponseInstruction::WithoutReplyPath(OnionMessageResponse {
message: response,
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<T: OnionMessageContents>(self, response: T) -> ResponseInstruction<T> {
+ 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.
/// `ResponseInstruction` represents instructions for responding to received messages.
pub enum ResponseInstruction<T: OnionMessageContents> {
+ /// Indicates that a response should be sent including a reply path for
+ /// the recipient to respond back.
+ WithReplyPath(OnionMessageResponse<T>),
/// Indicates that a response should be sent without including a reply path
/// for the recipient to respond back.
WithoutReplyPath(OnionMessageResponse<T>),
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,
.map_err(|_| SendError::PathNotFound)
}
+ fn create_blinded_path(&self) -> Result<BlindedPath, SendError> {
+ 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::<Vec<_>>();
+
+ 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<T: OnionMessageContents>(
&self, path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
log_suffix: fmt::Arguments
/// the response for delivery.
pub fn handle_onion_message_response<T: OnionMessageContents>(
&self, response: ResponseInstruction<T>
- ) {
- 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<Option<SendSuccess>, 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)]
|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);
},
}
},