From a6cd661712ed05421eef3af9f0088d73b7f50ceb Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 12 Sep 2023 16:14:16 -0500 Subject: [PATCH 01/15] Update OnionMessenger docs for Offers --- lightning/src/onion_message/messenger.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 1a0145441e2..27a0b0cd15b 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -35,9 +35,21 @@ use crate::io; use crate::sync::{Arc, Mutex}; use crate::prelude::*; -/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be -/// used to retrieve invoices and fulfill invoice requests from [offers]. Currently, only sending -/// and receiving custom onion messages is supported. +/// A sender, receiver and forwarder of [`OnionMessage`]s. +/// +/// # Handling Messages +/// +/// `OnionMessenger` implements [`OnionMessageHandler`], making it responsible for either forwarding +/// messages to peers or delegating to the appropriate handler for the message type. Currently, the +/// available handlers are: +/// * [`OffersMessageHandler`], for responding to [`InvoiceRequest`]s and paying [`Bolt12Invoice`]s +/// * [`CustomOnionMessageHandler`], for handling user-defined message types +/// +/// # Sending Messages +/// +/// [`OnionMessage`]s are sent initially using [`OnionMessenger::send_onion_message`]. When handling +/// a message, the matched handler may return a response message which `OnionMessenger` will send +/// on its behalf. /// /// # Example /// @@ -121,8 +133,9 @@ use crate::prelude::*; /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// -/// [offers]: -/// [`OnionMessenger`]: crate::onion_message::OnionMessenger +/// [`OnionMessage`]: crate::ln::msgs::OnionMessage +/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub struct OnionMessenger where ES::Target: EntropySource, From 86e2b0059fb1443146caddb7d752b44d512ad242 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 12 Sep 2023 16:57:15 -0500 Subject: [PATCH 02/15] Remove OnionMessageProvider OnionMessageProvider is a super-trait of OnionMessageHandler, but they don't need to be used separately. Additionally, the former is misplaced in the events module. Remove OnionMessageProvider and add it's only method, next_onion_message_for_peer, into OnionMessageHandler. --- lightning/src/events/mod.rs | 6 ------ lightning/src/ln/msgs.rs | 11 ++++++++--- lightning/src/ln/peer_handler.rs | 6 ++---- lightning/src/onion_message/functional_tests.rs | 1 - lightning/src/onion_message/messenger.rs | 12 ------------ 5 files changed, 10 insertions(+), 26 deletions(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index c8c736c1f71..ca55fde31c5 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1846,12 +1846,6 @@ pub trait MessageSendEventsProvider { fn get_and_clear_pending_msg_events(&self) -> Vec; } -/// A trait indicating an object may generate onion messages to send -pub trait OnionMessageProvider { - /// Gets the next pending onion message for the peer with the given node id. - fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option; -} - /// A trait indicating an object may generate events. /// /// Events are processed by passing an [`EventHandler`] to [`process_pending_events`]. diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index f86dc74befe..477524a9d54 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -49,7 +49,7 @@ use core::str::FromStr; use crate::io::{self, Cursor, Read}; use crate::io_extras::read_to_end; -use crate::events::{MessageSendEventsProvider, OnionMessageProvider}; +use crate::events::MessageSendEventsProvider; use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter; use crate::util::logger; use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize}; @@ -1497,10 +1497,14 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures; } -/// A trait to describe an object that can receive onion messages. -pub trait OnionMessageHandler : OnionMessageProvider { +/// A handler for received [`OnionMessage`]s and for providing generated ones to send. +pub trait OnionMessageHandler { /// Handle an incoming `onion_message` message from the given peer. fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage); + + /// Returns the next pending onion message for the peer with the given node id. + fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option; + /// Called when a connection is established with a peer. Can be used to track which peers /// advertise onion message support and are online. /// @@ -1508,6 +1512,7 @@ pub trait OnionMessageHandler : OnionMessageProvider { /// with us. Implementors should be somewhat conservative about doing so, however, as other /// message handlers may still wish to communicate with this peer. fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>; + /// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to /// drop and refuse to forward onion messages to this peer. fn peer_disconnected(&self, their_node_id: &PublicKey); diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 8e91023b1b1..a5a6939e167 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -19,7 +19,7 @@ use bitcoin::blockdata::constants::ChainHash; use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey}; use crate::sign::{KeysManager, NodeSigner, Recipient}; -use crate::events::{MessageSendEvent, MessageSendEventsProvider, OnionMessageProvider}; +use crate::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::ln::ChannelId; use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::msgs; @@ -107,11 +107,9 @@ impl RoutingMessageHandler for IgnoringMessageHandler { } fn processing_queue_high(&self) -> bool { false } } -impl OnionMessageProvider for IgnoringMessageHandler { - fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option { None } -} impl OnionMessageHandler for IgnoringMessageHandler { fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {} + fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option { None } fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init, _inbound: bool) -> Result<(), ()> { Ok(()) } fn peer_disconnected(&self, _their_node_id: &PublicKey) {} fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index c2d90ad60f9..33aebb0fd9f 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -10,7 +10,6 @@ //! Onion message testing and test utilities live here. use crate::blinded_path::BlindedPath; -use crate::events::OnionMessageProvider; use crate::ln::features::InitFeatures; use crate::ln::msgs::{self, DecodeError, OnionMessageHandler}; use crate::sign::{NodeSigner, Recipient}; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 27a0b0cd15b..67ba81e820f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -19,7 +19,6 @@ use crate::blinded_path::BlindedPath; use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs}; use crate::blinded_path::utils; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient}; -use crate::events::OnionMessageProvider; use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::msgs::{self, OnionMessageHandler}; use crate::ln::onion_utils; @@ -645,18 +644,7 @@ where features.set_onion_messages_optional(); features } -} -impl OnionMessageProvider -for OnionMessenger -where - ES::Target: EntropySource, - NS::Target: NodeSigner, - L::Target: Logger, - MR::Target: MessageRouter, - OMH::Target: OffersMessageHandler, - CMH::Target: CustomOnionMessageHandler, -{ fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { let mut pending_msgs = self.pending_messages.lock().unwrap(); if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) { From b78cb69de3f154da68144f2f447af030caf3b294 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 12 Sep 2023 17:18:49 -0500 Subject: [PATCH 03/15] Avoid overloading introduction_node_id --- lightning/src/onion_message/messenger.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 67ba81e820f..f271e2c20cc 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -268,7 +268,7 @@ pub enum PeeledOnion { } /// Create an onion message with contents `message` to the destination of `path`. -/// Returns (introduction_node_id, onion_msg) +/// Returns (first_node_id, onion_msg) pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, path: OnionMessagePath, message: OnionMessageContents, reply_path: Option, @@ -301,8 +301,8 @@ where let blinding_secret_bytes = entropy_source.get_secure_random_bytes(); let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted"); - let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 { - (intermediate_nodes[0], PublicKey::from_secret_key(&secp_ctx, &blinding_secret)) + let (first_node_id, blinding_point) = if let Some(first_node_id) = intermediate_nodes.first() { + (*first_node_id, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)) } else { match destination { Destination::Node(pk) => (pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)), @@ -318,7 +318,7 @@ where let onion_routing_packet = construct_onion_message_packet( packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?; - Ok((introduction_node_id, msgs::OnionMessage { + Ok((first_node_id, msgs::OnionMessage { blinding_point, onion_routing_packet })) @@ -456,14 +456,14 @@ where &self, path: OnionMessagePath, message: OnionMessageContents, reply_path: Option ) -> Result<(), SendError> { - let (introduction_node_id, onion_msg) = create_onion_message( + let (first_node_id, onion_msg) = create_onion_message( &self.entropy_source, &self.node_signer, &self.secp_ctx, path, message, reply_path )?; let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap(); - if outbound_buffer_full(&introduction_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) } - match pending_per_peer_msgs.entry(introduction_node_id) { + if outbound_buffer_full(&first_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) } + match pending_per_peer_msgs.entry(first_node_id) { hash_map::Entry::Vacant(_) => Err(SendError::InvalidFirstHop), hash_map::Entry::Occupied(mut e) => { e.get_mut().push_back(onion_msg); From a4894bd3caa31a77044ec5c62149249df56d3ec5 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 4 Oct 2023 15:23:34 -1000 Subject: [PATCH 04/15] Clean up onion messenger parameters and docs --- lightning/src/onion_message/messenger.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index f271e2c20cc..3b65bb4b16c 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -267,11 +267,15 @@ pub enum PeeledOnion { Receive(OnionMessageContents, Option<[u8; 32]>, Option) } -/// Create an onion message with contents `message` to the destination of `path`. -/// Returns (first_node_id, onion_msg) +/// 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. +/// +/// [`OnionMessage`]: msgs::OnionMessage pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, - path: OnionMessagePath, message: OnionMessageContents, reply_path: Option, + path: OnionMessagePath, contents: OnionMessageContents, reply_path: Option, ) -> Result<(PublicKey, msgs::OnionMessage), SendError> where ES::Target: EntropySource, @@ -284,7 +288,7 @@ where } } - if message.tlv_type() < 64 { return Err(SendError::InvalidMessage) } + if contents.tlv_type() < 64 { return Err(SendError::InvalidMessage) } // If we are sending straight to a blinded path and we are the introduction node, we need to // advance the blinded path by 1 hop so the second hop is the new introduction node. @@ -311,7 +315,7 @@ where } }; let (packet_payloads, packet_keys) = packet_payloads_and_keys( - &secp_ctx, &intermediate_nodes, destination, message, reply_path, &blinding_secret) + &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret) .map_err(|e| SendError::Secp256k1(e))?; let prng_seed = entropy_source.get_secure_random_bytes(); @@ -449,16 +453,16 @@ where } } - /// Send an onion message with contents `message` to the destination of `path`. + /// Sends an [`msgs::OnionMessage`] with the given `contents` for sending to the destination of + /// `path`. /// /// See [`OnionMessenger`] for example usage. pub fn send_onion_message( - &self, path: OnionMessagePath, message: OnionMessageContents, + &self, path: OnionMessagePath, contents: OnionMessageContents, reply_path: Option ) -> Result<(), SendError> { let (first_node_id, onion_msg) = create_onion_message( - &self.entropy_source, &self.node_signer, &self.secp_ctx, - path, message, reply_path + &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path )?; let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap(); From cfe6b952a8a00bd32dda985144b8b8060bb00c6a Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 13 Oct 2023 11:12:57 -0500 Subject: [PATCH 05/15] Import msgs::OnionMessage --- lightning/src/onion_message/messenger.rs | 33 ++++++++++-------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 3b65bb4b16c..6299d6178bc 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -20,7 +20,7 @@ use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs use crate::blinded_path::utils; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient}; use crate::ln::features::{InitFeatures, NodeFeatures}; -use crate::ln::msgs::{self, OnionMessageHandler}; +use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler}; use crate::ln::onion_utils; use crate::ln::peer_handler::IgnoringMessageHandler; pub use super::packet::{CustomOnionMessageContents, OnionMessageContents}; @@ -132,7 +132,6 @@ use crate::prelude::*; /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// -/// [`OnionMessage`]: crate::ln::msgs::OnionMessage /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub struct OnionMessenger @@ -147,7 +146,7 @@ where entropy_source: ES, node_signer: NS, logger: L, - pending_messages: Mutex>>, + pending_messages: Mutex>>, secp_ctx: Secp256k1, message_router: MR, offers_handler: OMH, @@ -155,12 +154,8 @@ where } /// A trait defining behavior for routing an [`OnionMessage`]. -/// -/// [`OnionMessage`]: msgs::OnionMessage pub trait MessageRouter { /// Returns a route for sending an [`OnionMessage`] to the given [`Destination`]. - /// - /// [`OnionMessage`]: msgs::OnionMessage fn find_path( &self, sender: PublicKey, peers: Vec, destination: Destination ) -> Result; @@ -177,7 +172,7 @@ impl MessageRouter for DefaultMessageRouter { } } -/// A path for sending an [`msgs::OnionMessage`]. +/// A path for sending an [`OnionMessage`]. #[derive(Clone)] pub struct OnionMessagePath { /// Nodes on the path between the sender and the destination. @@ -262,7 +257,7 @@ pub trait CustomOnionMessageHandler { /// or a Receive payload with decrypted contents. pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion - Forward(PublicKey, msgs::OnionMessage), + Forward(PublicKey, OnionMessage), /// Received onion message, with decrypted contents, path_id, and reply path Receive(OnionMessageContents, Option<[u8; 32]>, Option) } @@ -271,12 +266,10 @@ pub enum PeeledOnion { /// `path`. /// /// Returns both the node id of the peer to send the message to and the message itself. -/// -/// [`OnionMessage`]: msgs::OnionMessage pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, path: OnionMessagePath, contents: OnionMessageContents, reply_path: Option, -) -> Result<(PublicKey, msgs::OnionMessage), SendError> +) -> Result<(PublicKey, OnionMessage), SendError> where ES::Target: EntropySource, NS::Target: NodeSigner, @@ -322,7 +315,7 @@ where let onion_routing_packet = construct_onion_message_packet( packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?; - Ok((first_node_id, msgs::OnionMessage { + Ok((first_node_id, OnionMessage { blinding_point, onion_routing_packet })) @@ -332,7 +325,7 @@ where /// Returns either a Forward (another onion message), or Receive (decrypted content) pub fn peel_onion( node_signer: NS, secp_ctx: &Secp256k1, logger: L, custom_handler: CMH, - msg: &msgs::OnionMessage, + msg: &OnionMessage, ) -> Result::Target as CustomOnionMessageHandler>::CustomMessage>, ()> where NS::Target: NodeSigner, @@ -392,7 +385,7 @@ where hop_data: new_packet_bytes, hmac: next_hop_hmac, }; - let onion_message = msgs::OnionMessage { + let onion_message = OnionMessage { blinding_point: match next_blinding_override { Some(blinding_point) => blinding_point, None => { @@ -453,7 +446,7 @@ where } } - /// Sends an [`msgs::OnionMessage`] with the given `contents` for sending to the destination of + /// Sends an [`OnionMessage`] with the given `contents` for sending to the destination of /// `path`. /// /// See [`OnionMessenger`] for example usage. @@ -527,7 +520,7 @@ where } #[cfg(test)] - pub(super) fn release_pending_msgs(&self) -> HashMap> { + pub(super) fn release_pending_msgs(&self) -> HashMap> { let mut pending_msgs = self.pending_messages.lock().unwrap(); let mut msgs = HashMap::new(); // We don't want to disconnect the peers by removing them entirely from the original map, so we @@ -539,7 +532,7 @@ where } } -fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap>) -> bool { +fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap>) -> bool { const MAX_TOTAL_BUFFER_SIZE: usize = (1 << 20) * 128; const MAX_PER_PEER_BUFFER_SIZE: usize = (1 << 10) * 256; let mut total_buffered_bytes = 0; @@ -575,7 +568,7 @@ where /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but /// soon we'll delegate the onion message to a handler that can generate invoices or send /// payments. - fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) { + fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) { match peel_onion( &*self.node_signer, &self.secp_ctx, &*self.logger, &*self.custom_handler, msg ) { @@ -649,7 +642,7 @@ where features } - fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { + fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { let mut pending_msgs = self.pending_messages.lock().unwrap(); if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) { return msgs.pop_front() From 81c6147a9ec573abbf5b0cbe434e86626cc37112 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 13 Sep 2023 14:13:05 -0500 Subject: [PATCH 06/15] Generalize respond_with_onion_message OnionMessenger can send onion message responses from its handlers using respond_with_onion_message, which finds a path to the destination and enqueues the response for sending. Generalize this as it can be used not only for responses but for initial sends as well. --- fuzz/src/onion_message.rs | 4 +- lightning/src/onion_message/messenger.rs | 55 +++++++++++------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index d2e35cd45cd..67938aa7c39 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -216,9 +216,9 @@ mod tests { assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id None and a reply_path".to_string())), Some(&1)); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Responding to onion message with path_id None".to_string())), Some(&1)); + "Sending onion message when responding to onion message with path_id None".to_string())), Some(&1)); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Failed responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); + "Failed sending onion message when responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); } let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000"; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 6299d6178bc..626e2b05840 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -29,6 +29,7 @@ use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload 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}; @@ -469,52 +470,31 @@ where } } - fn respond_with_onion_message( - &self, response: OnionMessageContents, path_id: Option<[u8; 32]>, - reply_path: Option + fn find_path_and_enqueue_onion_message( + &self, contents: OnionMessageContents, destination: Destination, + 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, None) { + log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e); return; } } @@ -587,7 +567,22 @@ where }, }; if let Some(response) = response { - self.respond_with_onion_message(response, path_id, reply_path); + match reply_path { + Some(reply_path) => { + self.find_path_and_enqueue_onion_message( + response, Destination::BlindedPath(reply_path), format_args!( + "when responding to onion message with path_id {:02x?}", path_id + ) + ); + }, + None => { + log_trace!( + self.logger, + "Missing reply path when responding to onion message with path_id {:02x?}", + path_id + ); + }, + } } }, Ok(PeeledOnion::Forward(next_node_id, onion_message)) => { From 94573dda33c2e6bf55b2f28ffe7fbdf0a37f6edc Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 19 Sep 2023 10:59:53 -0500 Subject: [PATCH 07/15] Rename OnionMessageContents In preparation for needing the name OnionMessageContents for a trait to bound methods, rename it to ParsedOnionMessageContents. In the next commit, it's use will be limited to reading only, and the new trait will be a bound on method parameters instead. --- .../src/onion_message/functional_tests.rs | 32 +++++++++---------- lightning/src/onion_message/messenger.rs | 27 ++++++++-------- lightning/src/onion_message/mod.rs | 2 +- lightning/src/onion_message/packet.rs | 23 +++++++------ 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 33aebb0fd9f..319417e8065 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError}; +use super::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, ParsedOnionMessageContents, OnionMessagePath, OnionMessenger, SendError}; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; @@ -196,7 +196,7 @@ fn pass_along_path(path: &Vec) { #[test] fn one_hop() { let nodes = create_nodes(2); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); let path = OnionMessagePath { intermediate_nodes: vec![], @@ -210,7 +210,7 @@ fn one_hop() { #[test] fn two_unblinded_hops() { let nodes = create_nodes(3); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].get_node_pk()], @@ -224,7 +224,7 @@ fn two_unblinded_hops() { #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap(); @@ -241,7 +241,7 @@ fn two_unblinded_two_blinded() { #[test] fn three_blinded_hops() { let nodes = create_nodes(4); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap(); @@ -259,7 +259,7 @@ fn three_blinded_hops() { fn too_big_packet_error() { // Make sure we error as expected if a packet is too big to send. let nodes = create_nodes(2); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); let hop_node_id = nodes[1].get_node_pk(); let hops = vec![hop_node_id; 400]; @@ -285,7 +285,7 @@ fn we_are_intro_node() { destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap(); nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response); pass_along_path(&nodes); @@ -295,7 +295,7 @@ fn we_are_intro_node() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap(); nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response); nodes.remove(2); pass_along_path(&nodes); @@ -315,7 +315,7 @@ fn invalid_blinded_path_error() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); // 1 hop @@ -326,7 +326,7 @@ fn invalid_blinded_path_error() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); } @@ -342,7 +342,7 @@ fn reply_path() { destination: Destination::Node(nodes[3].get_node_pk()), }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); // Make sure the last node successfully decoded the reply path. @@ -358,7 +358,7 @@ fn reply_path() { }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); @@ -384,7 +384,7 @@ fn invalid_custom_message_type() { fn write(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() } } - let test_msg = OnionMessageContents::Custom(InvalidCustomMessage {}); + let test_msg = ParsedOnionMessageContents::Custom(InvalidCustomMessage {}); let path = OnionMessagePath { intermediate_nodes: vec![], destination: Destination::Node(nodes[1].get_node_pk()), @@ -402,9 +402,9 @@ fn peer_buffer_full() { destination: Destination::Node(nodes[1].get_node_pk()), }; for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger - nodes[0].messenger.send_onion_message(path.clone(), OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path.clone(), ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap(); } - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap_err(); assert_eq!(err, SendError::BufferFull); } @@ -425,7 +425,7 @@ fn many_hops() { intermediate_nodes, destination: Destination::Node(nodes[num_nodes-1].get_node_pk()), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap(); nodes[num_nodes-1].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 626e2b05840..074a1790f02 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -23,7 +23,7 @@ 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::{CustomOnionMessageContents, 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; @@ -60,7 +60,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::{CustomOnionMessageContents, Destination, MessageRouter, ParsedOnionMessageContents, OnionMessagePath, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; /// # use lightning::util::ser::{Writeable, Writer}; /// # use lightning::io; @@ -114,7 +114,7 @@ use crate::prelude::*; /// }; /// let reply_path = None; /// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// let message = ParsedOnionMessageContents::Custom(your_custom_message); /// onion_messenger.send_onion_message(path, message, reply_path); /// /// // Create a blinded path to yourself, for someone to send an onion message to. @@ -129,7 +129,7 @@ use crate::prelude::*; /// }; /// let reply_path = None; /// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// let message = ParsedOnionMessageContents::Custom(your_custom_message); /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// @@ -260,7 +260,7 @@ 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 @@ -269,7 +269,8 @@ pub enum PeeledOnion { /// Returns both the node id of the peer to send the message to and the message itself. pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, - path: OnionMessagePath, contents: OnionMessageContents, reply_path: Option, + path: OnionMessagePath, contents: ParsedOnionMessageContents, + reply_path: Option, ) -> Result<(PublicKey, OnionMessage), SendError> where ES::Target: EntropySource, @@ -452,7 +453,7 @@ where /// /// See [`OnionMessenger`] for example usage. pub fn send_onion_message( - &self, path: OnionMessagePath, contents: OnionMessageContents, + &self, path: OnionMessagePath, contents: ParsedOnionMessageContents, reply_path: Option ) -> Result<(), SendError> { let (first_node_id, onion_msg) = create_onion_message( @@ -471,7 +472,7 @@ where } fn find_path_and_enqueue_onion_message( - &self, contents: OnionMessageContents, destination: Destination, + &self, contents: ParsedOnionMessageContents, destination: Destination, log_suffix: fmt::Arguments ) { let sender = match self.node_signer.get_node_id(Recipient::Node) { @@ -557,13 +558,13 @@ where "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) => { + ParsedOnionMessageContents::Offers(msg) => { self.offers_handler.handle_message(msg) - .map(|msg| OnionMessageContents::Offers(msg)) + .map(|msg| ParsedOnionMessageContents::Offers(msg)) }, - OnionMessageContents::Custom(msg) => { + ParsedOnionMessageContents::Custom(msg) => { self.custom_handler.handle_custom_message(msg) - .map(|msg| OnionMessageContents::Custom(msg)) + .map(|msg| ParsedOnionMessageContents::Custom(msg)) }, }; if let Some(response) = response { @@ -684,7 +685,7 @@ pub type SimpleRefOnionMessenger<'a, 'b, 'c, L> = OnionMessenger< /// `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 + message: ParsedOnionMessageContents, 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); diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index fb2943425dd..59c4672126c 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, ParsedOnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub use self::offers::{OffersMessage, OffersMessageHandler}; pub use self::packet::Packet; pub(crate) use self::packet::ControlTlvs; diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index d19bd30edd2..c5411831ab7 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -110,38 +110,37 @@ pub(super) enum Payload { Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option, - message: OnionMessageContents, + message: ParsedOnionMessageContents, } } +/// The contents of an onion message as read from the wire. #[derive(Debug)] -/// The contents of an onion message. In the context of offers, this would be the invoice, invoice -/// request, or invoice error. -pub enum OnionMessageContents { +pub enum ParsedOnionMessageContents { /// A message related to BOLT 12 Offers. Offers(OffersMessage), /// A custom onion message specified by the user. Custom(T), } -impl OnionMessageContents { +impl ParsedOnionMessageContents { /// Returns the type that was used to decode the message payload. /// /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable pub fn tlv_type(&self) -> u64 { match self { - &OnionMessageContents::Offers(ref msg) => msg.tlv_type(), - &OnionMessageContents::Custom(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(), } } } /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable -impl Writeable for OnionMessageContents { +impl Writeable for ParsedOnionMessageContents { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { - OnionMessageContents::Offers(msg) => Ok(msg.write(w)?), - OnionMessageContents::Custom(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?), } } } @@ -236,12 +235,12 @@ ReadableArgs<(SharedSecret, &H, &L)> for Payload< { let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?; - message = Some(OnionMessageContents::Offers(msg)); + message = Some(ParsedOnionMessageContents::Offers(msg)); Ok(true) }, _ => match handler.read_custom_message(msg_type, msg_reader)? { Some(msg) => { - message = Some(OnionMessageContents::Custom(msg)); + message = Some(ParsedOnionMessageContents::Custom(msg)); Ok(true) }, None => Ok(false), From 840efd533468cf7cacd8b4bab27ce2db86b3eb93 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 19 Sep 2023 12:35:13 -0500 Subject: [PATCH 08/15] Generalize CustomOnionMessageContents trait Rename CustomOnionMessageContents to OnionMessageContents and use it as a trait bound on messages passed to OnionMessenger methods. This allows using the trait in an upcoming commit as a bound on the contents of PendingOnionMessage. Also, make ParsedOnionMessageContent implement OnionMessageContents so that Payload can be bounded by OnionMessageContents directly, but used when either reading a ParsedOnionMessageContent or writing a specific type of OnionMessageContents (e.g., OffersMessage). --- fuzz/src/onion_message.rs | 8 +- lightning/src/ln/peer_handler.rs | 4 +- .../src/onion_message/functional_tests.rs | 36 +++---- lightning/src/onion_message/messenger.rs | 100 ++++++++++-------- lightning/src/onion_message/mod.rs | 4 +- lightning/src/onion_message/offers.rs | 20 ++-- lightning/src/onion_message/packet.rs | 26 ++--- 7 files changed, 104 insertions(+), 94 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 67938aa7c39..57d4f697b3a 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessagePath, OnionMessenger}; +use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger}; use crate::utils::test_logger; @@ -84,7 +84,7 @@ struct TestCustomMessage {} const CUSTOM_MESSAGE_TYPE: u64 = 4242; const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; -impl CustomOnionMessageContents for TestCustomMessage { +impl OnionMessageContents for TestCustomMessage { fn tlv_type(&self) -> u64 { CUSTOM_MESSAGE_TYPE } @@ -216,9 +216,9 @@ mod tests { assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id None and a reply_path".to_string())), Some(&1)); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Sending onion message when responding to onion message with path_id None".to_string())), Some(&1)); + "Sending onion message when responding to Custom onion message with path_id None".to_string())), Some(&1)); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Failed sending onion message when responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); + "Failed sending onion message when responding to Custom onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); } let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000"; diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index a5a6939e167..84fb9748b46 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias}; use crate::util::atomic_counter::AtomicCounter; use crate::util::logger::Logger; @@ -131,7 +131,7 @@ impl CustomOnionMessageHandler for IgnoringMessageHandler { } } -impl CustomOnionMessageContents for Infallible { +impl OnionMessageContents for Infallible { fn tlv_type(&self) -> u64 { unreachable!(); } } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 319417e8065..0348e4afbf5 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, ParsedOnionMessageContents, OnionMessagePath, OnionMessenger, SendError}; +use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError}; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; @@ -77,7 +77,7 @@ const CUSTOM_RESPONSE_MESSAGE_TYPE: u64 = 4343; const CUSTOM_REQUEST_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; const CUSTOM_RESPONSE_MESSAGE_CONTENTS: [u8; 32] = [43; 32]; -impl CustomOnionMessageContents for TestCustomMessage { +impl OnionMessageContents for TestCustomMessage { fn tlv_type(&self) -> u64 { match self { TestCustomMessage::Request => CUSTOM_REQUEST_MESSAGE_TYPE, @@ -196,7 +196,7 @@ fn pass_along_path(path: &Vec) { #[test] fn one_hop() { let nodes = create_nodes(2); - let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let path = OnionMessagePath { intermediate_nodes: vec![], @@ -210,7 +210,7 @@ fn one_hop() { #[test] fn two_unblinded_hops() { let nodes = create_nodes(3); - let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].get_node_pk()], @@ -224,7 +224,7 @@ fn two_unblinded_hops() { #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); - let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap(); @@ -241,7 +241,7 @@ fn two_unblinded_two_blinded() { #[test] fn three_blinded_hops() { let nodes = create_nodes(4); - let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap(); @@ -259,7 +259,7 @@ fn three_blinded_hops() { fn too_big_packet_error() { // Make sure we error as expected if a packet is too big to send. let nodes = create_nodes(2); - let test_msg = ParsedOnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let hop_node_id = nodes[1].get_node_pk(); let hops = vec![hop_node_id; 400]; @@ -285,7 +285,7 @@ fn we_are_intro_node() { destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap(); nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response); pass_along_path(&nodes); @@ -295,7 +295,7 @@ fn we_are_intro_node() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response); nodes.remove(2); pass_along_path(&nodes); @@ -315,7 +315,7 @@ fn invalid_blinded_path_error() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); // 1 hop @@ -326,7 +326,7 @@ fn invalid_blinded_path_error() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); } @@ -342,7 +342,7 @@ fn reply_path() { destination: Destination::Node(nodes[3].get_node_pk()), }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg.clone(), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); // Make sure the last node successfully decoded the reply path. @@ -358,7 +358,7 @@ fn reply_path() { }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); @@ -373,7 +373,7 @@ fn invalid_custom_message_type() { let nodes = create_nodes(2); struct InvalidCustomMessage{} - impl CustomOnionMessageContents for InvalidCustomMessage { + impl OnionMessageContents for InvalidCustomMessage { fn tlv_type(&self) -> u64 { // Onion message contents must have a TLV >= 64. 63 @@ -384,7 +384,7 @@ fn invalid_custom_message_type() { fn write(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() } } - let test_msg = ParsedOnionMessageContents::Custom(InvalidCustomMessage {}); + let test_msg = InvalidCustomMessage {}; let path = OnionMessagePath { intermediate_nodes: vec![], destination: Destination::Node(nodes[1].get_node_pk()), @@ -402,9 +402,9 @@ fn peer_buffer_full() { destination: Destination::Node(nodes[1].get_node_pk()), }; for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger - nodes[0].messenger.send_onion_message(path.clone(), ParsedOnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path.clone(), test_msg.clone(), None).unwrap(); } - let err = nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap_err(); assert_eq!(err, SendError::BufferFull); } @@ -425,7 +425,7 @@ fn many_hops() { intermediate_nodes, destination: Destination::Node(nodes[num_nodes-1].get_node_pk()), }; - nodes[0].messenger.send_onion_message(path, ParsedOnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); nodes[num_nodes-1].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 074a1790f02..d6aa5c82ca5 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -23,7 +23,8 @@ 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, ParsedOnionMessageContents}; +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; @@ -60,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, ParsedOnionMessageContents, 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; @@ -101,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 @@ -113,8 +114,7 @@ use crate::prelude::*; /// destination: Destination::Node(destination_node_id), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = ParsedOnionMessageContents::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. @@ -128,8 +128,7 @@ use crate::prelude::*; /// destination: Destination::BlindedPath(blinded_path), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = ParsedOnionMessageContents::Custom(your_custom_message); +/// # let message = YourCustomMessage {}; /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// @@ -244,7 +243,7 @@ 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. fn handle_custom_message(&self, msg: Self::CustomMessage) -> Option; @@ -256,21 +255,20 @@ pub trait CustomOnionMessageHandler { /// 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(ParsedOnionMessageContents, 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: ParsedOnionMessageContents, - reply_path: Option, + path: OnionMessagePath, contents: T, reply_path: Option, ) -> Result<(PublicKey, OnionMessage), SendError> where ES::Target: EntropySource, @@ -361,7 +359,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)) @@ -452,9 +450,8 @@ where /// `path`. /// /// See [`OnionMessenger`] for example usage. - pub fn send_onion_message( - &self, path: OnionMessagePath, contents: ParsedOnionMessageContents, - 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 @@ -471,9 +468,25 @@ where } } - fn find_path_and_enqueue_onion_message( - &self, contents: ParsedOnionMessageContents, destination: Destination, - log_suffix: fmt::Arguments + 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), log_suffix + ); + }, + None => { + log_trace!(self.logger, "Missing reply path {}", log_suffix); + }, + } + } + } + + fn find_path_and_enqueue_onion_message( + &self, contents: T, destination: Destination, log_suffix: fmt::Arguments ) { let sender = match self.node_signer.get_node_id(Recipient::Node) { Ok(node_id) => node_id, @@ -557,33 +570,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 { + + match message { ParsedOnionMessageContents::Offers(msg) => { - self.offers_handler.handle_message(msg) - .map(|msg| 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 + ) + ); }, ParsedOnionMessageContents::Custom(msg) => { - self.custom_handler.handle_custom_message(msg) - .map(|msg| ParsedOnionMessageContents::Custom(msg)) - }, - }; - if let Some(response) = response { - match reply_path { - Some(reply_path) => { - self.find_path_and_enqueue_onion_message( - response, Destination::BlindedPath(reply_path), format_args!( - "when responding to onion message with path_id {:02x?}", path_id - ) - ); - }, - None => { - log_trace!( - self.logger, - "Missing reply path when responding to onion message with path_id {:02x?}", + 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 - ); - }, - } + ) + ); + }, } }, Ok(PeeledOnion::Forward(next_node_id, onion_message)) => { @@ -683,9 +689,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: ParsedOnionMessageContents, 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); @@ -761,7 +767,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 diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index 59c4672126c..dd92792316d 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -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::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, ParsedOnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub use self::offers::{OffersMessage, OffersMessageHandler}; -pub use self::packet::Packet; +pub use self::packet::{Packet, ParsedOnionMessageContents}; pub(crate) use self::packet::ControlTlvs; diff --git a/lightning/src/onion_message/offers.rs b/lightning/src/onion_message/offers.rs index de373bda1bc..f5945cd5505 100644 --- a/lightning/src/onion_message/offers.rs +++ b/lightning/src/onion_message/offers.rs @@ -16,6 +16,7 @@ use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::InvoiceRequest; use crate::offers::invoice::Bolt12Invoice; use crate::offers::parse::Bolt12ParseError; +use crate::onion_message::OnionMessageContents; use crate::util::logger::Logger; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -63,15 +64,6 @@ impl OffersMessage { } } - /// The TLV record type for the message as used in an `onionmsg_tlv` TLV stream. - pub fn tlv_type(&self) -> u64 { - match self { - OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE, - OffersMessage::Invoice(_) => INVOICE_TLV_TYPE, - OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE, - } - } - fn parse(tlv_type: u64, bytes: Vec) -> Result { match tlv_type { INVOICE_REQUEST_TLV_TYPE => Ok(Self::InvoiceRequest(InvoiceRequest::try_from(bytes)?)), @@ -81,6 +73,16 @@ impl OffersMessage { } } +impl OnionMessageContents for OffersMessage { + fn tlv_type(&self) -> u64 { + match self { + OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE, + OffersMessage::Invoice(_) => INVOICE_TLV_TYPE, + OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE, + } + } +} + impl Writeable for OffersMessage { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index c5411831ab7..19ca6eb963b 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -103,31 +103,33 @@ impl LengthReadable for Packet { /// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route /// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion /// message content itself, such as an invoice request. -pub(super) enum Payload { +pub(super) enum Payload { /// This payload is for an intermediate hop. Forward(ForwardControlTlvs), /// This payload is for the final hop. Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option, - message: ParsedOnionMessageContents, + message: T, } } -/// The contents of an onion message as read from the wire. +/// The contents of an [`OnionMessage`] as read from the wire. +/// +/// [`OnionMessage`]: crate::ln::msgs::OnionMessage #[derive(Debug)] -pub enum ParsedOnionMessageContents { +pub enum ParsedOnionMessageContents { /// A message related to BOLT 12 Offers. Offers(OffersMessage), /// A custom onion message specified by the user. Custom(T), } -impl ParsedOnionMessageContents { +impl OnionMessageContents for ParsedOnionMessageContents { /// Returns the type that was used to decode the message payload. /// /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable - pub fn tlv_type(&self) -> u64 { + fn tlv_type(&self) -> u64 { match self { &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(), &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(), @@ -136,7 +138,7 @@ impl ParsedOnionMessageContents { } /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable -impl Writeable for ParsedOnionMessageContents { +impl Writeable for ParsedOnionMessageContents { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?), @@ -145,8 +147,8 @@ impl Writeable for ParsedOnionMessageContents } } -/// The contents of a custom onion message. -pub trait CustomOnionMessageContents: Writeable { +/// The contents of an onion message. +pub trait OnionMessageContents: Writeable { /// Returns the TLV type identifying the message contents. MUST be >= 64. fn tlv_type(&self) -> u64; } @@ -172,7 +174,7 @@ pub(super) enum ReceiveControlTlvs { } // Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs. -impl Writeable for (Payload, [u8; 32]) { +impl Writeable for (Payload, [u8; 32]) { fn write(&self, w: &mut W) -> Result<(), io::Error> { match &self.0 { Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => { @@ -211,8 +213,8 @@ impl Writeable for (Payload, [u8; 32]) { } // Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV. -impl -ReadableArgs<(SharedSecret, &H, &L)> for Payload<::CustomMessage> { +impl ReadableArgs<(SharedSecret, &H, &L)> +for Payload::CustomMessage>> { fn read(r: &mut R, args: (SharedSecret, &H, &L)) -> Result { let (encrypted_tlvs_ss, handler, logger) = args; From 8b442fe4eb80ee43121c3a0f7a364698a296b713 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 13 Sep 2023 21:19:50 -0500 Subject: [PATCH 09/15] 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. --- fuzz/src/onion_message.rs | 5 +- lightning/src/ln/peer_handler.rs | 5 +- .../src/onion_message/functional_tests.rs | 5 +- lightning/src/onion_message/messenger.rs | 49 +++++++++++++++++-- lightning/src/onion_message/mod.rs | 2 +- lightning/src/onion_message/offers.rs | 11 +++++ 6 files changed, 70 insertions(+), 7 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 57d4f697b3a..6277037a194 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -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> { + vec![] + } } pub struct VecWriter(pub Vec); diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 84fb9748b46..fc574251f56 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -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(&self, _msg_type: u64, _buffer: &mut R) -> Result, msgs::DecodeError> where Self: Sized { Ok(None) } + fn release_pending_custom_messages(&self) -> Vec> { + vec![] + } } impl OnionMessageContents for Infallible { diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 0348e4afbf5..6540fc89cd0 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -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> { + vec![] + } } fn create_nodes(num_messengers: u8) -> Vec { diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index d6aa5c82ca5..d526ba9e3a2 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -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`]. @@ -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; /// 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) @@ -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( - &self, contents: T, destination: Destination, log_suffix: fmt::Arguments + &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, @@ -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 { + // 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() diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index dd92792316d..be800822e4d 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -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; diff --git a/lightning/src/onion_message/offers.rs b/lightning/src/onion_message/offers.rs index f5945cd5505..254db7b81bd 100644 --- a/lightning/src/onion_message/offers.rs +++ b/lightning/src/onion_message/offers.rs @@ -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; + + /// 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> { vec![] } } /// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`]. From 6dc42235baaa22320ad78d3e05fab31edad99328 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 11 Sep 2023 17:40:43 -0500 Subject: [PATCH 10/15] Allow sending onion messages to 1-hop blinded path This allows for specifying the introduction node as the message recipient. --- fuzz/src/onion_message.rs | 13 --------- lightning/src/blinded_path/mod.rs | 17 +++++++---- .../src/onion_message/functional_tests.rs | 29 +++++++++++-------- lightning/src/onion_message/messenger.rs | 6 ++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 6277037a194..fcc8dc3cad2 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -211,19 +211,6 @@ mod tests { #[test] fn test_no_onion_message_breakage() { - let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01ae0276020000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000e0101022a0000000000000000000000000000014551231950b75fc4402da1732fc9bebf00109500000000000000000000000000000004106d000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000"; - let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; - super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger); - { - let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Received an onion message with path_id None and a reply_path".to_string())), Some(&1)); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Sending onion message when responding to Custom onion message with path_id None".to_string())), Some(&1)); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Failed sending onion message when responding to Custom onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); - } - let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(two_unblinded_hops_om).unwrap(), &logger); diff --git a/lightning/src/blinded_path/mod.rs b/lightning/src/blinded_path/mod.rs index 89bf7ce5d2f..896999799fb 100644 --- a/lightning/src/blinded_path/mod.rs +++ b/lightning/src/blinded_path/mod.rs @@ -56,15 +56,22 @@ pub struct BlindedHop { } impl BlindedPath { + /// Create a one-hop blinded path for a message. + pub fn one_hop_for_message( + recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1 + ) -> Result { + Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx) + } + /// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node /// pubkey in `node_pks` will be the destination node. /// - /// Errors if less than two hops are provided or if `node_pk`(s) are invalid. + /// Errors if no hops are provided or if `node_pk`(s) are invalid. // TODO: make all payloads the same size with padding + add dummy hops - pub fn new_for_message - (node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1) -> Result - { - if node_pks.len() < 2 { return Err(()) } + pub fn new_for_message( + node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1 + ) -> Result { + if node_pks.is_empty() { return Err(()) } let blinding_secret_bytes = entropy_source.get_secure_random_bytes(); let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted"); let introduction_node_id = node_pks[0]; diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 6540fc89cd0..0193001b30c 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -197,7 +197,7 @@ fn pass_along_path(path: &Vec) { } #[test] -fn one_hop() { +fn one_unblinded_hop() { let nodes = create_nodes(2); let test_msg = TestCustomMessage::Response; @@ -224,6 +224,22 @@ fn two_unblinded_hops() { pass_along_path(&nodes); } +#[test] +fn one_blinded_hop() { + let nodes = create_nodes(2); + let test_msg = TestCustomMessage::Response; + + let secp_ctx = Secp256k1::new(); + let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap(); + let path = OnionMessagePath { + intermediate_nodes: vec![], + destination: Destination::BlindedPath(blinded_path), + }; + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); + nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response); + pass_along_path(&nodes); +} + #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); @@ -320,17 +336,6 @@ fn invalid_blinded_path_error() { }; let err = nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); - - // 1 hop - let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap(); - blinded_path.blinded_hops.remove(0); - assert_eq!(blinded_path.blinded_hops.len(), 1); - let path = OnionMessagePath { - intermediate_nodes: vec![], - destination: Destination::BlindedPath(blinded_path), - }; - let err = nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap_err(); - assert_eq!(err, SendError::TooFewBlindedHops); } #[test] diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index d526ba9e3a2..e50d3d67e45 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -225,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, @@ -299,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); } } From 622f7f2f7943295ccf9393547390dc09852c8143 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 17 Oct 2023 10:47:48 -0500 Subject: [PATCH 11/15] Remove outdated docs --- lightning/src/onion_message/messenger.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index e50d3d67e45..79bd5724364 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -583,9 +583,6 @@ where OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, { - /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but - /// soon we'll delegate the onion message to a handler that can generate invoices or send - /// payments. fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) { match peel_onion( &*self.node_signer, &self.secp_ctx, &*self.logger, &*self.custom_handler, msg From 11fb9c486b2ebbd57ed41ad92b91a7f92b55236a Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 17 Oct 2023 09:59:39 -0500 Subject: [PATCH 12/15] Await for invoices using an absolute expiry PendingOutboundPayment::AwaitingInvoice counts the number of timer ticks that have passed awaiting a Bolt12Invoice for an InvoiceRequest. When a constant INVOICE_REQUEST_TIMEOUT_TICKS has passed, the payment is forgotten. However, this mechanism is insufficient for the Refund scenario, where the Refund's expiration should be used instead. Change AwaitingInvoice to store an absolute expiry instead. When removing stale payments, pass the `SystemTime` in `std` and the highest block time minus two hours in `no-std`. --- lightning/src/ln/channelmanager.rs | 17 ++++- lightning/src/ln/outbound_payment.rs | 96 +++++++++++++++++----------- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d1a1208c809..3abbd477910 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4791,6 +4791,10 @@ where /// with the current [`ChannelConfig`]. /// * Removing peers which have disconnected but and no longer have any channels. /// * Force-closing and removing channels which have not completed establishment in a timely manner. + /// * Forgetting about stale outbound payments, either those that have already been fulfilled + /// or those awaiting an invoice that hasn't been delivered in the necessary amount of time. + /// The latter is determined using the system clock in `std` and the block time minus two + /// hours in `no-std`. /// /// Note that this may cause reentrancy through [`chain::Watch::update_channel`] calls or feerate /// estimate fetches. @@ -5019,7 +5023,18 @@ where self.finish_close_channel(shutdown_res); } - self.pending_outbound_payments.remove_stale_payments(&self.pending_events); + #[cfg(feature = "std")] + let duration_since_epoch = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); + #[cfg(not(feature = "std"))] + let duration_since_epoch = Duration::from_secs( + self.highest_seen_timestamp.load(Ordering::Acquire).saturating_sub(7200) as u64 + ); + + self.pending_outbound_payments.remove_stale_payments( + duration_since_epoch, &self.pending_events + ); // Technically we don't need to do this here, but if we have holding cell entries in a // channel that need freeing, it's better to do that here and block a background task diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index d6d9f7aaca0..d679036c1d6 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -29,6 +29,7 @@ use crate::util::ser::ReadableArgs; use core::fmt::{self, Display, Formatter}; use core::ops::Deref; +use core::time::Duration; use crate::prelude::*; use crate::sync::Mutex; @@ -39,12 +40,6 @@ use crate::sync::Mutex; /// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7; -/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until an invoice request without -/// a response is timed out. -/// -/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred -const INVOICE_REQUEST_TIMEOUT_TICKS: u8 = 3; - /// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102 /// and later, also stores information for retrying the payment. pub(crate) enum PendingOutboundPayment { @@ -52,7 +47,7 @@ pub(crate) enum PendingOutboundPayment { session_privs: HashSet<[u8; 32]>, }, AwaitingInvoice { - timer_ticks_without_response: u8, + absolute_expiry: Duration, retry_strategy: Retry, max_total_routing_fee_msat: Option, }, @@ -1274,15 +1269,16 @@ impl OutboundPayments { } #[allow(unused)] - pub(super) fn add_new_awaiting_invoice( - &self, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option + fn add_new_awaiting_invoice( + &self, payment_id: PaymentId, absolute_expiry: Duration, retry_strategy: Retry, + max_total_routing_fee_msat: Option ) -> Result<(), ()> { let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap(); match pending_outbounds.entry(payment_id) { hash_map::Entry::Occupied(_) => Err(()), hash_map::Entry::Vacant(entry) => { entry.insert(PendingOutboundPayment::AwaitingInvoice { - timer_ticks_without_response: 0, + absolute_expiry, retry_strategy, max_total_routing_fee_msat, }); @@ -1511,14 +1507,15 @@ impl OutboundPayments { } pub(super) fn remove_stale_payments( - &self, pending_events: &Mutex)>>) + &self, duration_since_epoch: Duration, + pending_events: &Mutex)>>) { let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap(); #[cfg(not(invreqfailed))] let pending_events = pending_events.lock().unwrap(); #[cfg(invreqfailed)] let mut pending_events = pending_events.lock().unwrap(); - pending_outbound_payments.retain(|payment_id, payment| { + pending_outbound_payments.retain(|payment_id, payment| match payment { // If an outbound payment was completed, and no pending HTLCs remain, we should remove it // from the map. However, if we did that immediately when the last payment HTLC is claimed, // this could race the user making a duplicate send_payment call and our idempotency @@ -1526,7 +1523,7 @@ impl OutboundPayments { // removal. This should be more than sufficient to ensure the idempotency of any // `send_payment` calls that were made at the same time the `PaymentSent` event was being // processed. - if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment { + PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } => { let mut no_remaining_entries = session_privs.is_empty(); if no_remaining_entries { for (ev, _) in pending_events.iter() { @@ -1550,9 +1547,9 @@ impl OutboundPayments { *timer_ticks_without_htlcs = 0; true } - } else if let PendingOutboundPayment::AwaitingInvoice { timer_ticks_without_response, .. } = payment { - *timer_ticks_without_response += 1; - if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS { + }, + PendingOutboundPayment::AwaitingInvoice { absolute_expiry, .. } => { + if duration_since_epoch < *absolute_expiry { true } else { #[cfg(invreqfailed)] @@ -1561,7 +1558,8 @@ impl OutboundPayments { ); false } - } else { true } + }, + _ => true, }); } @@ -1778,7 +1776,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (2, payment_hash, required), }, (5, AwaitingInvoice) => { - (0, timer_ticks_without_response, required), + (0, absolute_expiry, required), (2, retry_strategy, required), (4, max_total_routing_fee_msat, option), }, @@ -1794,14 +1792,14 @@ mod tests { use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use core::time::Duration; + use crate::events::{Event, PathFailure, PaymentFailureReason}; use crate::ln::PaymentHash; use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; use crate::ln::features::{ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError}; use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure}; - #[cfg(invreqfailed)] - use crate::ln::outbound_payment::INVOICE_REQUEST_TIMEOUT_TICKS; use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY; use crate::offers::offer::OfferBuilder; use crate::offers::test_utils::*; @@ -2011,20 +2009,28 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; + let tick_interval = 10; assert!(!outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); - for _ in 0..INVOICE_REQUEST_TIMEOUT_TICKS { - outbound_payments.remove_stale_payments(&pending_events); + for seconds_since_epoch in (0..absolute_expiry).step_by(tick_interval) { + let duration_since_epoch = Duration::from_secs(seconds_since_epoch); + outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events); + assert!(outbound_payments.has_pending_payments()); assert!(pending_events.lock().unwrap().is_empty()); } - outbound_payments.remove_stale_payments(&pending_events); + let duration_since_epoch = Duration::from_secs(absolute_expiry); + outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events); + assert!(!outbound_payments.has_pending_payments()); assert!(!pending_events.lock().unwrap().is_empty()); assert_eq!( @@ -2034,13 +2040,16 @@ mod tests { assert!(pending_events.lock().unwrap().is_empty()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None) - .is_err() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None + ).is_err() ); } @@ -2050,10 +2059,13 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; assert!(!outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2081,9 +2093,12 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2129,6 +2144,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2140,9 +2156,11 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); - assert!(outbound_payments.add_new_awaiting_invoice( - payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) - .is_ok() + assert!( + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), + Some(invoice.amount_msats() / 100 + 50_000) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2185,6 +2203,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2196,9 +2215,11 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); - assert!(outbound_payments.add_new_awaiting_invoice( - payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) - .is_ok() + assert!( + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), + Some(invoice.amount_msats() / 100 + 50_000) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2241,6 +2262,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2292,7 +2314,9 @@ mod tests { assert!(pending_events.lock().unwrap().is_empty()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), Some(1234)).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), Some(1234) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); From 7c6e62f42359407443c9baacd4ac12ffb2d772b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 13 Feb 2023 21:54:37 -0600 Subject: [PATCH 13/15] Stateless offer and refund builder utilities Add utility functions to ChannelManager for creating OfferBuilder, and RefundBuilder such that derived keys are used for the signing pubkey and payer id, respectively. This allows for stateless verification of any InvoiceRequest and Invoice messages. Later, blinded paths can be included in the returned builders. Also tracks future payments using the given PaymentId such that the corresponding Invoice is paid only once. --- lightning/src/ln/channelmanager.rs | 56 ++++++++++++++++++++++++++++ lightning/src/ln/outbound_payment.rs | 3 +- lightning/src/offers/offer.rs | 31 ++++++++++++++- lightning/src/offers/parse.rs | 2 + lightning/src/offers/refund.rs | 30 ++++++++++++++- 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 3abbd477910..3810cfb8171 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -55,6 +55,9 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use crate::ln::outbound_payment; use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs}; use crate::ln::wire::Encode; +use crate::offers::offer::{DerivedMetadata, OfferBuilder}; +use crate::offers::parse::Bolt12SemanticError; +use crate::offers::refund::RefundBuilder; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner}; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::wakers::{Future, Notifier}; @@ -7123,6 +7126,59 @@ where } } + /// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the + /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will + /// not have an expiration unless otherwise set on the builder. + /// + /// [`Offer`]: crate::offers::offer::Offer + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + pub fn create_offer_builder( + &self, description: String + ) -> OfferBuilder { + let node_id = self.get_our_node_id(); + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + + // TODO: Set blinded paths + OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx) + .chain_hash(self.chain_hash) + } + + /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the + /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. The builder will + /// have the provided expiration set. Any changes to the expiration on the returned builder will + /// not be honored by [`ChannelManager`]. + /// + /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. + /// + /// [`Refund`]: crate::offers::refund::Refund + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + pub fn create_refund_builder( + &self, description: String, amount_msats: u64, absolute_expiry: Duration, + payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option + ) -> Result, Bolt12SemanticError> { + let node_id = self.get_our_node_id(); + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + + // TODO: Set blinded paths + let builder = RefundBuilder::deriving_payer_id( + description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id + )? + .chain_hash(self.chain_hash) + .absolute_expiry(absolute_expiry); + + self.pending_outbound_payments + .add_new_awaiting_invoice( + payment_id, absolute_expiry, retry_strategy, max_total_routing_fee_msat, + ) + .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + + Ok(builder) + } + /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing /// to pay us. /// diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index d679036c1d6..19faad07bbd 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -1268,8 +1268,7 @@ impl OutboundPayments { (payment, onion_session_privs) } - #[allow(unused)] - fn add_new_awaiting_invoice( + pub(super) fn add_new_awaiting_invoice( &self, payment_id: PaymentId, absolute_expiry: Duration, retry_strategy: Retry, max_total_routing_fee_msat: Option ) -> Result<(), ()> { diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index 60621b9dc74..ab95d5b192f 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -13,6 +13,8 @@ //! published as a QR code to be scanned by a customer. The customer uses the offer to request an //! invoice from the merchant to be paid. //! +//! # Example +//! //! ``` //! extern crate bitcoin; //! extern crate core; @@ -65,6 +67,14 @@ //! # Ok(()) //! # } //! ``` +//! +//! # Note +//! +//! If constructing an [`Offer`] for use with a [`ChannelManager`], use +//! [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`]. +//! +//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager +//! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; @@ -132,6 +142,14 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> { /// while the offer is valid. /// /// Use a different pubkey per offer to avoid correlating offers. + /// + /// # Note + /// + /// If constructing an [`Offer`] for use with a [`ChannelManager`], use + /// [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`]. + /// + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder pub fn new(description: String, signing_pubkey: PublicKey) -> Self { OfferBuilder { offer: OfferContents { @@ -191,9 +209,18 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> { /// See [`Offer::chains`] on how this relates to the payment currency. /// /// Successive calls to this method will add another chain hash. - pub fn chain(mut self, network: Network) -> Self { + pub fn chain(self, network: Network) -> Self { + self.chain_hash(ChainHash::using_genesis_block(network)) + } + + /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of + /// [`Network::Bitcoin`] is assumed to be the only one supported. + /// + /// See [`Offer::chains`] on how this relates to the payment currency. + /// + /// Successive calls to this method will add another chain hash. + pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self { let chains = self.offer.chains.get_or_insert_with(Vec::new); - let chain = ChainHash::using_genesis_block(network); if !chains.contains(&chain) { chains.push(chain); } diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index e9477086ee9..c85c2f326b0 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -179,6 +179,8 @@ pub enum Bolt12SemanticError { MissingPayerMetadata, /// A payer id was expected but was missing. MissingPayerId, + /// The payment id for a refund or request is already in use. + DuplicatePaymentId, /// Blinded paths were expected but were missing. MissingPaths, /// The blinded payinfo given does not match the number of blinded path hops. diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index 4b4572b4df9..ecafb2bb5c7 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -18,6 +18,8 @@ //! [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest //! [`Offer`]: crate::offers::offer::Offer //! +//! # Example +//! //! ``` //! extern crate bitcoin; //! extern crate core; @@ -70,6 +72,14 @@ //! # Ok(()) //! # } //! ``` +//! +//! # Note +//! +//! If constructing a [`Refund`] for use with a [`ChannelManager`], use +//! [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`]. +//! +//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager +//! [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; @@ -120,6 +130,14 @@ impl<'a> RefundBuilder<'a, secp256k1::SignOnly> { /// /// Additionally, sets the required [`Refund::description`], [`Refund::payer_metadata`], and /// [`Refund::amount_msats`]. + /// + /// # Note + /// + /// If constructing a [`Refund`] for use with a [`ChannelManager`], use + /// [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`]. + /// + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder pub fn new( description: String, metadata: Vec, payer_id: PublicKey, amount_msats: u64 ) -> Result { @@ -206,8 +224,16 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> { /// called, [`Network::Bitcoin`] is assumed. /// /// Successive calls to this method will override the previous setting. - pub fn chain(mut self, network: Network) -> Self { - self.refund.chain = Some(ChainHash::using_genesis_block(network)); + pub fn chain(self, network: Network) -> Self { + self.chain_hash(ChainHash::using_genesis_block(network)) + } + + /// Sets the [`Refund::chain`] of the given [`ChainHash`] for paying an invoice. If not called, + /// [`Network::Bitcoin`] is assumed. + /// + /// Successive calls to this method will override the previous setting. + pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self { + self.refund.chain = Some(chain); self } From 80ae66ac17266cc0f68f054d4547da5db0973e40 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Fri, 3 Mar 2023 09:38:45 -0600 Subject: [PATCH 14/15] Include a one-hop blinded path in Offer and Refund While this doesn't add much privacy over not including any blinded paths, it allows us to exercise code for receiving on blinded paths. --- lightning/src/blinded_path/mod.rs | 4 ++-- lightning/src/ln/channelmanager.rs | 27 ++++++++++++++++++++++++--- lightning/src/routing/router.rs | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lightning/src/blinded_path/mod.rs b/lightning/src/blinded_path/mod.rs index 896999799fb..1415836c01f 100644 --- a/lightning/src/blinded_path/mod.rs +++ b/lightning/src/blinded_path/mod.rs @@ -57,7 +57,7 @@ pub struct BlindedHop { impl BlindedPath { /// Create a one-hop blinded path for a message. - pub fn one_hop_for_message( + pub fn one_hop_for_message( recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1 ) -> Result { Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx) @@ -68,7 +68,7 @@ impl BlindedPath { /// /// Errors if no hops are provided or if `node_pk`(s) are invalid. // TODO: make all payloads the same size with padding + add dummy hops - pub fn new_for_message( + pub fn new_for_message( node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1 ) -> Result { if node_pks.is_empty() { return Err(()) } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 3810cfb8171..113d6902155 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -30,6 +30,7 @@ use bitcoin::secp256k1::{SecretKey,PublicKey}; use bitcoin::secp256k1::Secp256k1; use bitcoin::{LockTime, secp256k1, Sequence}; +use crate::blinded_path::BlindedPath; use crate::chain; use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock}; use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator}; @@ -7130,6 +7131,11 @@ where /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will /// not have an expiration unless otherwise set on the builder. /// + /// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the + /// introduction node and a derived signing pubkey for recipient privacy. As such, currently, + /// the node must be announced. Otherwise, there is no way to find a path to the introduction + /// node in order to send the [`InvoiceRequest`]. + /// /// [`Offer`]: crate::offers::offer::Offer /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn create_offer_builder( @@ -7139,10 +7145,11 @@ where let expanded_key = &self.inbound_payment_key; let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; + let path = self.create_one_hop_blinded_path(); - // TODO: Set blinded paths OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx) .chain_hash(self.chain_hash) + .path(path) } /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the @@ -7152,6 +7159,11 @@ where /// /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. /// + /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as + /// the introduction node and a derived payer id for sender privacy. As such, currently, the + /// node must be announced. Otherwise, there is no way to find a path to the introduction node + /// in order to send the [`Bolt12Invoice`]. + /// /// [`Refund`]: crate::offers::refund::Refund /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub fn create_refund_builder( @@ -7162,13 +7174,14 @@ where let expanded_key = &self.inbound_payment_key; let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; + let path = self.create_one_hop_blinded_path(); - // TODO: Set blinded paths let builder = RefundBuilder::deriving_payer_id( description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id )? .chain_hash(self.chain_hash) - .absolute_expiry(absolute_expiry); + .absolute_expiry(absolute_expiry) + .path(path); self.pending_outbound_payments .add_new_awaiting_invoice( @@ -7279,6 +7292,14 @@ where inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key) } + /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction + /// node. + fn create_one_hop_blinded_path(&self) -> BlindedPath { + let entropy_source = self.entropy_source.deref(); + let secp_ctx = &self.secp_ctx; + BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap() + } + /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids /// are used when constructing the phantom invoice's route hints. /// diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 4c8a31bd735..104f4c93c38 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -90,6 +90,7 @@ pub trait Router { &self, payer: &PublicKey, route_params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs ) -> Result; + /// Finds a [`Route`] for a payment between the given `payer` and a payee. /// /// The `payee` and the payment's value are given in [`RouteParameters::payment_params`] From 905028b61581f1d78225de9e518297f53159a5f4 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 18 Oct 2023 18:24:07 -0500 Subject: [PATCH 15/15] Clean up peel_onion name, parameters, and docs For consistency with other functions and doc cleanliness. --- lightning/src/onion_message/messenger.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 79bd5724364..8960058fa9f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -344,11 +344,13 @@ where })) } -/// Decode one layer of an incoming onion message -/// Returns either a Forward (another onion message), or Receive (decrypted content) -pub fn peel_onion( - node_signer: NS, secp_ctx: &Secp256k1, logger: L, custom_handler: CMH, - msg: &OnionMessage, +/// Decode one layer of an incoming [`OnionMessage`]. +/// +/// Returns either the next layer of the onion for forwarding or the decrypted content for the +/// receiver. +pub fn peel_onion_message( + msg: &OnionMessage, secp_ctx: &Secp256k1, node_signer: NS, logger: L, + custom_handler: CMH, ) -> Result::Target as CustomOnionMessageHandler>::CustomMessage>, ()> where NS::Target: NodeSigner, @@ -584,8 +586,8 @@ where CMH::Target: CustomOnionMessageHandler, { fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) { - match peel_onion( - &*self.node_signer, &self.secp_ctx, &*self.logger, &*self.custom_handler, msg + match peel_onion_message( + msg, &self.secp_ctx, &*self.node_signer, &*self.logger, &*self.custom_handler ) { Ok(PeeledOnion::Receive(message, path_id, reply_path)) => { log_trace!(self.logger,