Enqueue onion messages in handlers

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.
This commit is contained in:
Jeffrey Czyz 2023-09-13 21:19:50 -05:00
parent 840efd5334
commit 8b442fe4eb
No known key found for this signature in database
GPG key ID: 3A4E08275D5E96D2
6 changed files with 70 additions and 7 deletions

View 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>);

View 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 {

View 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> {

View 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()

View 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;

View 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`].