X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;ds=sidebyside;f=lightning%2Fsrc%2Fonion_message%2Fmessenger.rs;h=e50d3d67e4505d2dd784fc45500ece57e59b7fb8;hb=6dc42235baaa22320ad78d3e05fab31edad99328;hp=6299d6178bc96902070ce5ed2bff85e2ba1e1fe1;hpb=cfe6b952a8a00bd32dda985144b8b8060bb00c6a;p=rust-lightning diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 6299d617..e50d3d67 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -23,12 +23,14 @@ use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler}; use crate::ln::onion_utils; use crate::ln::peer_handler::IgnoringMessageHandler; -pub use super::packet::{CustomOnionMessageContents, OnionMessageContents}; +pub use super::packet::OnionMessageContents; +use super::packet::ParsedOnionMessageContents; use super::offers::OffersMessageHandler; use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN}; use crate::util::logger::Logger; use crate::util::ser::Writeable; +use core::fmt; use core::ops::Deref; use crate::io; use crate::sync::{Arc, Mutex}; @@ -59,7 +61,7 @@ use crate::prelude::*; /// # use lightning::blinded_path::BlindedPath; /// # use lightning::sign::KeysManager; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; -/// # use lightning::onion_message::{CustomOnionMessageContents, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger}; +/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; /// # use lightning::util::ser::{Writeable, Writer}; /// # use lightning::io; @@ -100,7 +102,7 @@ use crate::prelude::*; /// // Write your custom onion message to `w` /// } /// } -/// impl CustomOnionMessageContents for YourCustomMessage { +/// impl OnionMessageContents for YourCustomMessage { /// fn tlv_type(&self) -> u64 { /// # let your_custom_message_type = 42; /// your_custom_message_type @@ -112,8 +114,7 @@ use crate::prelude::*; /// destination: Destination::Node(destination_node_id), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// # let message = YourCustomMessage {}; /// onion_messenger.send_onion_message(path, message, reply_path); /// /// // Create a blinded path to yourself, for someone to send an onion message to. @@ -127,8 +128,7 @@ use crate::prelude::*; /// destination: Destination::BlindedPath(blinded_path), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// # let message = YourCustomMessage {}; /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// @@ -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 { + /// 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, +} + /// A trait defining behavior for routing an [`OnionMessage`]. pub trait MessageRouter { /// Returns a route for sending an [`OnionMessage`] to the given [`Destination`]. @@ -210,8 +225,8 @@ pub enum SendError { /// Because implementations such as Eclair will drop onion messages where the message packet /// exceeds 32834 bytes, we refuse to send messages where the packet exceeds this size. TooBigPacket, - /// The provided [`Destination`] was an invalid [`BlindedPath`], due to having fewer than two - /// blinded hops. + /// The provided [`Destination`] was an invalid [`BlindedPath`] due to not having any blinded + /// hops. TooFewBlindedHops, /// Our next-hop peer was offline or does not support onion message forwarding. InvalidFirstHop, @@ -243,32 +258,40 @@ pub enum SendError { pub trait CustomOnionMessageHandler { /// The message known to the handler. To support multiple message types, you may want to make this /// an enum with a variant for each supported message. - type CustomMessage: CustomOnionMessageContents; + 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; /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the /// message type is unknown. fn read_custom_message(&self, message_type: u64, buffer: &mut R) -> Result, 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>; } /// A processed incoming onion message, containing either a Forward (another onion message) /// or a Receive payload with decrypted contents. -pub enum PeeledOnion { +pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion Forward(PublicKey, OnionMessage), /// Received onion message, with decrypted contents, path_id, and reply path - Receive(OnionMessageContents, Option<[u8; 32]>, Option) + Receive(ParsedOnionMessageContents, Option<[u8; 32]>, Option) } /// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of /// `path`. /// /// Returns both the node id of the peer to send the message to and the message itself. -pub fn create_onion_message( +pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, - path: OnionMessagePath, contents: OnionMessageContents, reply_path: Option, + path: OnionMessagePath, contents: T, reply_path: Option, ) -> Result<(PublicKey, OnionMessage), SendError> where ES::Target: EntropySource, @@ -276,7 +299,7 @@ where { let OnionMessagePath { intermediate_nodes, mut destination } = path; if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination { - if blinded_hops.len() < 2 { + if blinded_hops.is_empty() { return Err(SendError::TooFewBlindedHops); } } @@ -359,7 +382,7 @@ where onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, (control_tlvs_ss, custom_handler.deref(), logger.deref()) ) { - Ok((Payload::Receive::<<::Target as CustomOnionMessageHandler>::CustomMessage> { + Ok((Payload::Receive::::Target as CustomOnionMessageHandler>::CustomMessage>> { message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, }, None)) => { Ok(PeeledOnion::Receive(message, path_id, reply_path)) @@ -450,9 +473,8 @@ where /// `path`. /// /// See [`OnionMessenger`] for example usage. - pub fn send_onion_message( - &self, path: OnionMessagePath, contents: OnionMessageContents, - reply_path: Option + pub fn send_onion_message( + &self, path: OnionMessagePath, contents: T, reply_path: Option ) -> Result<(), SendError> { let (first_node_id, onion_msg) = create_onion_message( &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path @@ -469,52 +491,48 @@ where } } - fn respond_with_onion_message( - &self, response: OnionMessageContents, path_id: Option<[u8; 32]>, - reply_path: Option + fn handle_onion_message_response( + &self, response: Option, reply_path: Option, log_suffix: fmt::Arguments + ) { + if let Some(response) = response { + match reply_path { + Some(reply_path) => { + self.find_path_and_enqueue_onion_message( + response, Destination::BlindedPath(reply_path), None, log_suffix + ); + }, + None => { + log_trace!(self.logger, "Missing reply path {}", log_suffix); + }, + } + } + } + + fn find_path_and_enqueue_onion_message( + &self, contents: T, destination: Destination, reply_path: Option, + log_suffix: fmt::Arguments ) { let sender = match self.node_signer.get_node_id(Recipient::Node) { Ok(node_id) => node_id, Err(_) => { - log_warn!( - self.logger, "Unable to retrieve node id when responding to onion message with \ - path_id {:02x?}", path_id - ); + log_warn!(self.logger, "Unable to retrieve node id {}", log_suffix); return; } }; let peers = self.pending_messages.lock().unwrap().keys().copied().collect(); - - let destination = match reply_path { - Some(reply_path) => Destination::BlindedPath(reply_path), - None => { - log_trace!( - self.logger, "Missing reply path when responding to onion message with path_id \ - {:02x?}", path_id - ); - return; - }, - }; - let path = match self.message_router.find_path(sender, peers, destination) { Ok(path) => path, Err(()) => { - log_trace!( - self.logger, "Failed to find path when responding to onion message with \ - path_id {:02x?}", path_id - ); + log_trace!(self.logger, "Failed to find path {}", log_suffix); return; }, }; - log_trace!(self.logger, "Responding to onion message with path_id {:02x?}", path_id); + log_trace!(self.logger, "Sending onion message {}", log_suffix); - if let Err(e) = self.send_onion_message(path, response, None) { - log_trace!( - self.logger, "Failed responding to onion message with path_id {:02x?}: {:?}", - path_id, e - ); + if let Err(e) = self.send_onion_message(path, contents, reply_path) { + log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e); return; } } @@ -576,18 +594,26 @@ where log_trace!(self.logger, "Received an onion message with path_id {:02x?} and {} reply_path", path_id, if reply_path.is_some() { "a" } else { "no" }); - let response = match message { - OnionMessageContents::Offers(msg) => { - self.offers_handler.handle_message(msg) - .map(|msg| OnionMessageContents::Offers(msg)) + + match message { + ParsedOnionMessageContents::Offers(msg) => { + let response = self.offers_handler.handle_message(msg); + self.handle_onion_message_response( + response, reply_path, format_args!( + "when responding to Offers onion message with path_id {:02x?}", + path_id + ) + ); }, - OnionMessageContents::Custom(msg) => { - self.custom_handler.handle_custom_message(msg) - .map(|msg| OnionMessageContents::Custom(msg)) + ParsedOnionMessageContents::Custom(msg) => { + let response = self.custom_handler.handle_custom_message(msg); + self.handle_onion_message_response( + response, reply_path, format_args!( + "when responding to Custom onion message with path_id {:02x?}", + path_id + ) + ); }, - }; - if let Some(response) = response { - self.respond_with_onion_message(response, path_id, reply_path); } }, Ok(PeeledOnion::Forward(next_node_id, onion_message)) => { @@ -642,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 { + // 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() @@ -687,9 +732,9 @@ pub type SimpleRefOnionMessenger<'a, 'b, 'c, L> = OnionMessenger< /// Construct onion packet payloads and keys for sending an onion message along the given /// `unblinded_path` to the given `destination`. -fn packet_payloads_and_keys( - secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, - message: OnionMessageContents, mut reply_path: Option, session_priv: &SecretKey +fn packet_payloads_and_keys( + secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, message: T, + mut reply_path: Option, session_priv: &SecretKey ) -> Result<(Vec<(Payload, [u8; 32])>, Vec), secp256k1::Error> { let num_hops = unblinded_path.len() + destination.num_hops(); let mut payloads = Vec::with_capacity(num_hops); @@ -765,7 +810,7 @@ fn packet_payloads_and_keys(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { +fn construct_onion_message_packet(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { // Spec rationale: // "`len` allows larger messages to be sent than the standard 1300 bytes allowed for an HTLC // onion, but this should be used sparingly as it is reduces anonymity set, hence the