Enqueue onion messages in handlers
authorJeffrey Czyz <jkczyz@gmail.com>
Thu, 14 Sep 2023 02:19:50 +0000 (21:19 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Wed, 18 Oct 2023 23:31:16 +0000 (18:31 -0500)
When constructing onion messages to send initially (opposed to replying
to one from a handler), the user must construct an OnionMessagePath first
before calling OnionMessener::send_onion_message. Additionally, having a
reference to OnionMessener isn't always desirable. For instance, in an
upcoming commit, ChannelManager will implement OffersMessageHandler,
which OnionMessenger needs a reference to. If ChannelManager had a
reference to OnionMessenger, too, there would be a dependency cycle.

Instead, modify OffersMessageHandler and CustomOnionMessageHandler's
interfaces to include a method for releasing pending onion messages.
That way, ChannelManager may, for instance, construct and enqueue an
InvoiceRequest for sending without needing a reference to
OnionMessenger.

Additionally, OnionMessenger has responsibility for path finding just as
it does when replying to messages from a handler. It performs this when
extracting messages from the handlers before returning the next message
to send to a peer.

fuzz/src/onion_message.rs
lightning/src/ln/peer_handler.rs
lightning/src/onion_message/functional_tests.rs
lightning/src/onion_message/messenger.rs
lightning/src/onion_message/mod.rs
lightning/src/onion_message/offers.rs

index 57d4f697b3a4abffcefe8e341e25b0cd9437d3e4..6277037a1946d5ebb160bf623b41338943ad7cfd 100644 (file)
@@ -14,7 +14,7 @@ use lightning::offers::invoice_request::UnsignedInvoiceRequest;
 use lightning::util::test_channel_signer::TestChannelSigner;
 use lightning::util::logger::Logger;
 use lightning::util::ser::{Readable, Writeable, Writer};
-use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger};
+use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage};
 
 use crate::utils::test_logger;
 
@@ -108,6 +108,9 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler {
                buffer.read_to_end(&mut buf)?;
                return Ok(Some(TestCustomMessage {}))
        }
+       fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>> {
+               vec![]
+       }
 }
 
 pub struct VecWriter(pub Vec<u8>);
index 84fb9748b4631cd4f49d17c3e2f0f9db680765db..fc574251f561300d77435dee3ba22e5a2261a76f 100644 (file)
@@ -29,7 +29,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
 use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
 use crate::ln::wire;
 use crate::ln::wire::{Encode, Type};
-use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, PendingOnionMessage, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
 use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias};
 use crate::util::atomic_counter::AtomicCounter;
 use crate::util::logger::Logger;
@@ -129,6 +129,9 @@ impl CustomOnionMessageHandler for IgnoringMessageHandler {
        fn read_custom_message<R: io::Read>(&self, _msg_type: u64, _buffer: &mut R) -> Result<Option<Infallible>, msgs::DecodeError> where Self: Sized {
                Ok(None)
        }
+       fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Infallible>> {
+               vec![]
+       }
 }
 
 impl OnionMessageContents for Infallible {
index 0348e4afbf5f4d9ea8eb492e7e67388927f01cc5..6540fc89cd0032160799bc9d109cf4f5d7b60442 100644 (file)
@@ -15,7 +15,7 @@ use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
 use crate::sign::{NodeSigner, Recipient};
 use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
 use crate::util::test_utils;
-use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError};
+use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
 
 use bitcoin::network::constants::Network;
 use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
@@ -148,6 +148,9 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler {
                        _ => Ok(None),
                }
        }
+       fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>> {
+               vec![]
+       }
 }
 
 fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
index d6aa5c82ca5bc526118b539c53b95eedbeb37688..d526ba9e3a2dfb1c226a7cdf5adef4276d69e24f 100644 (file)
@@ -153,6 +153,21 @@ where
        custom_handler: CMH,
 }
 
+/// An [`OnionMessage`] for [`OnionMessenger`] to send.
+///
+/// These are obtained when released from [`OnionMessenger`]'s handlers after which they are
+/// enqueued for sending.
+pub struct PendingOnionMessage<T: OnionMessageContents> {
+       /// The message contents to send in an [`OnionMessage`].
+       pub contents: T,
+
+       /// The destination of the message.
+       pub destination: Destination,
+
+       /// A reply path to include in the [`OnionMessage`] for a response.
+       pub reply_path: Option<BlindedPath>,
+}
+
 /// A trait defining behavior for routing an [`OnionMessage`].
 pub trait MessageRouter {
        /// Returns a route for sending an [`OnionMessage`] to the given [`Destination`].
@@ -246,11 +261,19 @@ pub trait CustomOnionMessageHandler {
        type CustomMessage: OnionMessageContents;
 
        /// Called with the custom message that was received, returning a response to send, if any.
+       ///
+       /// The returned [`Self::CustomMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
        fn handle_custom_message(&self, msg: Self::CustomMessage) -> Option<Self::CustomMessage>;
 
        /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the
        /// message type is unknown.
        fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError>;
+
+       /// Releases any [`Self::CustomMessage`]s that need to be sent.
+       ///
+       /// Typically, this is used for messages initiating a message flow rather than in response to
+       /// another message. The latter should use the return value of [`Self::handle_custom_message`].
+       fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>>;
 }
 
 /// A processed incoming onion message, containing either a Forward (another onion message)
@@ -475,7 +498,7 @@ where
                        match reply_path {
                                Some(reply_path) => {
                                        self.find_path_and_enqueue_onion_message(
-                                               response, Destination::BlindedPath(reply_path), log_suffix
+                                               response, Destination::BlindedPath(reply_path), None, log_suffix
                                        );
                                },
                                None => {
@@ -486,7 +509,8 @@ where
        }
 
        fn find_path_and_enqueue_onion_message<T: OnionMessageContents>(
-               &self, contents: T, destination: Destination, log_suffix: fmt::Arguments
+               &self, contents: T, destination: Destination, reply_path: Option<BlindedPath>,
+               log_suffix: fmt::Arguments
        ) {
                let sender = match self.node_signer.get_node_id(Recipient::Node) {
                        Ok(node_id) => node_id,
@@ -507,7 +531,7 @@ where
 
                log_trace!(self.logger, "Sending onion message {}", log_suffix);
 
-               if let Err(e) = self.send_onion_message(path, contents, None) {
+               if let Err(e) = self.send_onion_message(path, contents, reply_path) {
                        log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e);
                        return;
                }
@@ -644,7 +668,26 @@ where
                features
        }
 
+       // Before returning any messages to send for the peer, this method will see if any messages were
+       // enqueued in the handler by users, find a path to the corresponding blinded path's introduction
+       // node, and then enqueue the message for sending to the first peer in the full path.
        fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<OnionMessage> {
+               // Enqueue any initiating `OffersMessage`s to send.
+               for message in self.offers_handler.release_pending_messages() {
+                       let PendingOnionMessage { contents, destination, reply_path } = message;
+                       self.find_path_and_enqueue_onion_message(
+                               contents, destination, reply_path, format_args!("when sending OffersMessage")
+                       );
+               }
+
+               // Enqueue any initiating `CustomMessage`s to send.
+               for message in self.custom_handler.release_pending_custom_messages() {
+                       let PendingOnionMessage { contents, destination, reply_path } = message;
+                       self.find_path_and_enqueue_onion_message(
+                               contents, destination, reply_path, format_args!("when sending CustomMessage")
+                       );
+               }
+
                let mut pending_msgs = self.pending_messages.lock().unwrap();
                if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) {
                        return msgs.pop_front()
index dd92792316dd3272be742003b97e82b833687014..be800822e4d76f673473111c82515c01f3a9bbfa 100644 (file)
@@ -27,7 +27,7 @@ mod packet;
 mod functional_tests;
 
 // Re-export structs so they can be imported with just the `onion_message::` module prefix.
-pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, PendingOnionMessage, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
 pub use self::offers::{OffersMessage, OffersMessageHandler};
 pub use self::packet::{Packet, ParsedOnionMessageContents};
 pub(crate) use self::packet::ControlTlvs;
index f5945cd55054dc6c368a668fcf4aa777f15d6a21..254db7b81bdf12d89c3a1b739e4da7e06faa5d18 100644 (file)
@@ -17,6 +17,7 @@ use crate::offers::invoice_request::InvoiceRequest;
 use crate::offers::invoice::Bolt12Invoice;
 use crate::offers::parse::Bolt12ParseError;
 use crate::onion_message::OnionMessageContents;
+use crate::onion_message::messenger::PendingOnionMessage;
 use crate::util::logger::Logger;
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
 
@@ -33,7 +34,17 @@ const INVOICE_ERROR_TLV_TYPE: u64 = 68;
 pub trait OffersMessageHandler {
        /// Handles the given message by either responding with an [`Bolt12Invoice`], sending a payment,
        /// or replying with an error.
+       ///
+       /// The returned [`OffersMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
+       ///
+       /// [`OnionMessenger`]: crate::onion_message::OnionMessenger
        fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage>;
+
+       /// Releases any [`OffersMessage`]s that need to be sent.
+       ///
+       /// Typically, this is used for messages initiating a payment flow rather than in response to
+       /// another message. The latter should use the return value of [`Self::handle_message`].
+       fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> { vec![] }
 }
 
 /// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`].