Avoid overloading introduction_node_id
[rust-lightning] / lightning / src / onion_message / messenger.rs
index 1a6e2614b72910976e8aef5ff434dc10eff495bf..f271e2c20cc215147e481fc724f518d7f3b2e56e 100644 (file)
@@ -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;
@@ -35,9 +34,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 +132,9 @@ use crate::prelude::*;
 /// onion_messenger.send_onion_message(path, message, reply_path);
 /// ```
 ///
-/// [offers]: <https://github.com/lightning/bolts/pull/798>
-/// [`OnionMessenger`]: crate::onion_message::OnionMessenger
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
 pub struct OnionMessenger<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
 where
        ES::Target: EntropySource,
@@ -246,6 +258,167 @@ pub trait CustomOnionMessageHandler {
        fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError>;
 }
 
+/// A processed incoming onion message, containing either a Forward (another onion message)
+/// or a Receive payload with decrypted contents.
+pub enum PeeledOnion<CM: CustomOnionMessageContents> {
+       /// Forwarded onion, with the next node id and a new onion
+       Forward(PublicKey, msgs::OnionMessage),
+       /// Received onion message, with decrypted contents, path_id, and reply path
+       Receive(OnionMessageContents<CM>, Option<[u8; 32]>, Option<BlindedPath>)
+}
+
+/// Create an onion message with contents `message` to the destination of `path`.
+/// Returns (first_node_id, onion_msg)
+pub fn create_onion_message<ES: Deref, NS: Deref, T: CustomOnionMessageContents>(
+       entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1<secp256k1::All>,
+       path: OnionMessagePath, message: OnionMessageContents<T>, reply_path: Option<BlindedPath>,
+) -> Result<(PublicKey, msgs::OnionMessage), SendError>
+where
+       ES::Target: EntropySource,
+       NS::Target: NodeSigner,
+{
+       let OnionMessagePath { intermediate_nodes, mut destination } = path;
+       if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination {
+               if blinded_hops.len() < 2 {
+                       return Err(SendError::TooFewBlindedHops);
+               }
+       }
+
+       if message.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.
+       if intermediate_nodes.len() == 0 {
+               if let Destination::BlindedPath(ref mut blinded_path) = destination {
+                       let our_node_id = node_signer.get_node_id(Recipient::Node)
+                               .map_err(|()| SendError::GetNodeIdFailed)?;
+                       if blinded_path.introduction_node_id == our_node_id {
+                               advance_path_by_one(blinded_path, node_signer, &secp_ctx)
+                                       .map_err(|()| SendError::BlindedPathAdvanceFailed)?;
+                       }
+               }
+       }
+
+       let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
+       let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
+       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)),
+                       Destination::BlindedPath(BlindedPath { introduction_node_id, blinding_point, .. }) =>
+                               (introduction_node_id, blinding_point),
+               }
+       };
+       let (packet_payloads, packet_keys) = packet_payloads_and_keys(
+               &secp_ctx, &intermediate_nodes, destination, message, reply_path, &blinding_secret)
+               .map_err(|e| SendError::Secp256k1(e))?;
+
+       let prng_seed = entropy_source.get_secure_random_bytes();
+       let onion_routing_packet = construct_onion_message_packet(
+               packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?;
+
+       Ok((first_node_id, msgs::OnionMessage {
+               blinding_point,
+               onion_routing_packet
+       }))
+}
+
+/// Decode one layer of an incoming onion message
+/// Returns either a Forward (another onion message), or Receive (decrypted content)
+pub fn peel_onion<NS: Deref, L: Deref, CMH: Deref>(
+       node_signer: NS, secp_ctx: &Secp256k1<secp256k1::All>, logger: L, custom_handler: CMH,
+       msg: &msgs::OnionMessage,
+) -> Result<PeeledOnion<<<CMH>::Target as CustomOnionMessageHandler>::CustomMessage>, ()>
+where
+       NS::Target: NodeSigner,
+       L::Target: Logger,
+       CMH::Target: CustomOnionMessageHandler,
+{
+       let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
+               Ok(ss) => ss,
+               Err(e) =>  {
+                       log_error!(logger, "Failed to retrieve node secret: {:?}", e);
+                       return Err(());
+               }
+       };
+       let onion_decode_ss = {
+               let blinding_factor = {
+                       let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
+                       hmac.input(control_tlvs_ss.as_ref());
+                       Hmac::from_engine(hmac).into_inner()
+               };
+               match node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
+                       Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
+               {
+                       Ok(ss) => ss.secret_bytes(),
+                       Err(()) => {
+                               log_trace!(logger, "Failed to compute onion packet shared secret");
+                               return Err(());
+                       }
+               }
+       };
+       match onion_utils::decode_next_untagged_hop(
+               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::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage> {
+                       message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
+               }, None)) => {
+                       Ok(PeeledOnion::Receive(message, path_id, reply_path))
+               },
+               Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
+                       next_node_id, next_blinding_override
+               })), Some((next_hop_hmac, new_packet_bytes)))) => {
+                       // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
+                       // blinded hop and this onion message is destined for us. In this situation, we should keep
+                       // unwrapping the onion layers to get to the final payload. Since we don't have the option
+                       // of creating blinded paths with dummy hops currently, we should be ok to not handle this
+                       // for now.
+                       let new_pubkey = match onion_utils::next_hop_pubkey(&secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
+                               Ok(pk) => pk,
+                               Err(e) => {
+                                       log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e);
+                                       return Err(())
+                               }
+                       };
+                       let outgoing_packet = Packet {
+                               version: 0,
+                               public_key: new_pubkey,
+                               hop_data: new_packet_bytes,
+                               hmac: next_hop_hmac,
+                       };
+                       let onion_message = msgs::OnionMessage {
+                               blinding_point: match next_blinding_override {
+                                       Some(blinding_point) => blinding_point,
+                                       None => {
+                                               match onion_utils::next_hop_pubkey(
+                                                       &secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
+                                               ) {
+                                                       Ok(bp) => bp,
+                                                       Err(e) => {
+                                                               log_trace!(logger, "Failed to compute next blinding point: {}", e);
+                                                               return Err(())
+                                                       }
+                                               }
+                                       }
+                               },
+                               onion_routing_packet: outgoing_packet,
+                       };
+
+                       Ok(PeeledOnion::Forward(next_node_id, onion_message))
+               },
+               Err(e) => {
+                       log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
+                       Err(())
+               },
+               _ => {
+                       log_trace!(logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
+                       Err(())
+               },
+       }
+}
+
 impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
 OnionMessenger<ES, NS, L, MR, OMH, CMH>
 where
@@ -283,53 +456,17 @@ where
                &self, path: OnionMessagePath, message: OnionMessageContents<T>,
                reply_path: Option<BlindedPath>
        ) -> Result<(), SendError> {
-               let OnionMessagePath { intermediate_nodes, mut destination } = path;
-               if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination {
-                       if blinded_hops.len() < 2 {
-                               return Err(SendError::TooFewBlindedHops);
-                       }
-               }
-
-               if message.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.
-               if intermediate_nodes.len() == 0 {
-                       if let Destination::BlindedPath(ref mut blinded_path) = destination {
-                               let our_node_id = self.node_signer.get_node_id(Recipient::Node)
-                                       .map_err(|()| SendError::GetNodeIdFailed)?;
-                               if blinded_path.introduction_node_id == our_node_id {
-                                       advance_path_by_one(blinded_path, &self.node_signer, &self.secp_ctx)
-                                               .map_err(|()| SendError::BlindedPathAdvanceFailed)?;
-                               }
-                       }
-               }
-
-               let blinding_secret_bytes = self.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(&self.secp_ctx, &blinding_secret))
-               } else {
-                       match destination {
-                               Destination::Node(pk) => (pk, PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret)),
-                               Destination::BlindedPath(BlindedPath { introduction_node_id, blinding_point, .. }) =>
-                                       (introduction_node_id, blinding_point),
-                       }
-               };
-               let (packet_payloads, packet_keys) = packet_payloads_and_keys(
-                       &self.secp_ctx, &intermediate_nodes, destination, message, reply_path, &blinding_secret)
-                       .map_err(|e| SendError::Secp256k1(e))?;
-
-               let prng_seed = self.entropy_source.get_secure_random_bytes();
-               let onion_routing_packet = construct_onion_message_packet(
-                       packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?;
+               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(msgs::OnionMessage { blinding_point, onion_routing_packet });
+                               e.get_mut().push_back(onion_msg);
                                Ok(())
                        }
                }
@@ -435,40 +572,13 @@ where
        /// 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) {
-               let control_tlvs_ss = match self.node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
-                       Ok(ss) => ss,
-                       Err(e) =>  {
-                               log_error!(self.logger, "Failed to retrieve node secret: {:?}", e);
-                               return
-                       }
-               };
-               let onion_decode_ss = {
-                       let blinding_factor = {
-                               let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
-                               hmac.input(control_tlvs_ss.as_ref());
-                               Hmac::from_engine(hmac).into_inner()
-                       };
-                       match self.node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
-                               Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
-                       {
-                               Ok(ss) => ss.secret_bytes(),
-                               Err(()) => {
-                                       log_trace!(self.logger, "Failed to compute onion packet shared secret");
-                                       return
-                               }
-                       }
-               };
-               match onion_utils::decode_next_untagged_hop(
-                       onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
-                       (control_tlvs_ss, &*self.custom_handler, &*self.logger)
+               match peel_onion(
+                       &*self.node_signer, &self.secp_ctx, &*self.logger, &*self.custom_handler, msg
                ) {
-                       Ok((Payload::Receive::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage> {
-                               message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
-                       }, None)) => {
+                       Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
                                log_trace!(self.logger,
                                        "Received an onion message with path_id {:02x?} and {} reply_path",
                                                path_id, if reply_path.is_some() { "a" } else { "no" });
-
                                let response = match message {
                                        OnionMessageContents::Offers(msg) => {
                                                self.offers_handler.handle_message(msg)
@@ -479,50 +589,11 @@ where
                                                        .map(|msg| OnionMessageContents::Custom(msg))
                                        },
                                };
-
                                if let Some(response) = response {
                                        self.respond_with_onion_message(response, path_id, reply_path);
                                }
                        },
-                       Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
-                               next_node_id, next_blinding_override
-                       })), Some((next_hop_hmac, new_packet_bytes)))) => {
-                               // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
-                               // blinded hop and this onion message is destined for us. In this situation, we should keep
-                               // unwrapping the onion layers to get to the final payload. Since we don't have the option
-                               // of creating blinded paths with dummy hops currently, we should be ok to not handle this
-                               // for now.
-                               let new_pubkey = match onion_utils::next_hop_pubkey(&self.secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
-                                       Ok(pk) => pk,
-                                       Err(e) => {
-                                               log_trace!(self.logger, "Failed to compute next hop packet pubkey: {}", e);
-                                               return
-                                       }
-                               };
-                               let outgoing_packet = Packet {
-                                       version: 0,
-                                       public_key: new_pubkey,
-                                       hop_data: new_packet_bytes,
-                                       hmac: next_hop_hmac,
-                               };
-                               let onion_message = msgs::OnionMessage {
-                                       blinding_point: match next_blinding_override {
-                                               Some(blinding_point) => blinding_point,
-                                               None => {
-                                                       match onion_utils::next_hop_pubkey(
-                                                               &self.secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
-                                                       ) {
-                                                               Ok(bp) => bp,
-                                                               Err(e) => {
-                                                                       log_trace!(self.logger, "Failed to compute next blinding point: {}", e);
-                                                                       return
-                                                               }
-                                                       }
-                                               }
-                                       },
-                                       onion_routing_packet: outgoing_packet,
-                               };
-
+                       Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
                                let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
                                if outbound_buffer_full(&next_node_id, &pending_per_peer_msgs) {
                                        log_trace!(self.logger, "Dropping forwarded onion message to peer {:?}: outbound buffer full", next_node_id);
@@ -541,15 +612,12 @@ where
                                                e.get_mut().push_back(onion_message);
                                                log_trace!(self.logger, "Forwarding an onion message to peer {}", next_node_id);
                                        }
-                               };
+                               }
                        },
                        Err(e) => {
-                               log_trace!(self.logger, "Errored decoding onion message packet: {:?}", e);
-                       },
-                       _ => {
-                               log_trace!(self.logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
-                       },
-               };
+                               log_error!(self.logger, "Failed to process onion message {:?}", e);
+                       }
+               }
        }
 
        fn peer_connected(&self, their_node_id: &PublicKey, init: &msgs::Init, _inbound: bool) -> Result<(), ()> {
@@ -576,18 +644,7 @@ where
                features.set_onion_messages_optional();
                features
        }
-}
 
-impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref> OnionMessageProvider
-for OnionMessenger<ES, NS, L, MR, OMH, CMH>
-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<msgs::OnionMessage> {
                let mut pending_msgs = self.pending_messages.lock().unwrap();
                if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) {