X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fonion_message%2Fmessenger.rs;h=1d7a730fa3625126097fd6d25de150e5ba46c742;hb=d25d55a682dbd4a3c7fcbaa05744fa002e571bca;hp=5334d6db25e2e88d4906c896c7549c53a9d50d4e;hpb=7002180261d495f15c12f9a341af1e7dbcac2990;p=rust-lightning diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 5334d6db..1d7a730f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -15,7 +15,7 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; -use crate::blinded_path::{BlindedPath, IntroductionNode}; +use crate::blinded_path::{BlindedPath, IntroductionNode, NodeIdLookUp}; use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, NextHop, ReceiveTlvs}; use crate::blinded_path::utils; use crate::events::{Event, EventHandler, EventsProvider}; @@ -23,7 +23,7 @@ use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress}; use crate::ln::onion_utils; -use crate::routing::gossip::{NetworkGraph, NodeId}; +use crate::routing::gossip::{NetworkGraph, NodeId, ReadOnlyNetworkGraph}; use super::packet::OnionMessageContents; use super::packet::ParsedOnionMessageContents; use super::offers::OffersMessageHandler; @@ -70,7 +70,7 @@ pub(super) const MAX_TIMER_TICKS: usize = 2; /// # use bitcoin::hashes::_export::_core::time::Duration; /// # use bitcoin::hashes::hex::FromHex; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self}; -/// # use lightning::blinded_path::BlindedPath; +/// # use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp}; /// # use lightning::sign::{EntropySource, KeysManager}; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; /// # use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath, OnionMessenger}; @@ -111,14 +111,15 @@ pub(super) const MAX_TIMER_TICKS: usize = 2; /// # let hop_node_id1 = PublicKey::from_secret_key(&secp_ctx, &node_secret); /// # let (hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1); /// # let destination_node_id = hop_node_id1; +/// # let node_id_lookup = EmptyNodeIdLookUp {}; /// # let message_router = Arc::new(FakeMessageRouter {}); /// # let custom_message_handler = IgnoringMessageHandler {}; /// # let offers_message_handler = IgnoringMessageHandler {}; /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your /// // ChannelManager. /// let onion_messenger = OnionMessenger::new( -/// &keys_manager, &keys_manager, logger, message_router, &offers_message_handler, -/// &custom_message_handler +/// &keys_manager, &keys_manager, logger, &node_id_lookup, message_router, +/// &offers_message_handler, &custom_message_handler /// ); /// # #[derive(Debug)] @@ -155,11 +156,12 @@ pub(super) const MAX_TIMER_TICKS: usize = 2; /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice -pub struct OnionMessenger +pub struct OnionMessenger where ES::Target: EntropySource, NS::Target: NodeSigner, L::Target: Logger, + NL::Target: NodeIdLookUp, MR::Target: MessageRouter, OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, @@ -169,6 +171,7 @@ where logger: L, message_recipients: Mutex>, secp_ctx: Secp256k1, + node_id_lookup: NL, message_router: MR, offers_handler: OMH, custom_handler: CMH, @@ -318,15 +321,21 @@ where ES::Target: EntropySource, { fn find_path( - &self, sender: PublicKey, peers: Vec, destination: Destination + &self, sender: PublicKey, peers: Vec, mut destination: Destination ) -> Result { - let first_node = destination.first_node(); + let network_graph = self.network_graph.deref().read_only(); + destination.resolve(&network_graph); + + let first_node = match destination.first_node() { + Some(first_node) => first_node, + None => return Err(()), + }; + if peers.contains(&first_node) || sender == first_node { Ok(OnionMessagePath { intermediate_nodes: vec![], destination, first_node_addresses: None }) } else { - let network_graph = self.network_graph.deref().read_only(); let node_announcement = network_graph .node(&NodeId::from_pubkey(&first_node)) .and_then(|node_info| node_info.announcement_info.as_ref()) @@ -416,11 +425,11 @@ pub struct OnionMessagePath { impl OnionMessagePath { /// Returns the first node in the path. - pub fn first_node(&self) -> PublicKey { + pub fn first_node(&self) -> Option { self.intermediate_nodes .first() .copied() - .unwrap_or_else(|| self.destination.first_node()) + .or_else(|| self.destination.first_node()) } } @@ -434,6 +443,22 @@ pub enum Destination { } impl Destination { + /// Attempts to resolve the [`IntroductionNode::DirectedShortChannelId`] of a + /// [`Destination::BlindedPath`] to a [`IntroductionNode::NodeId`], if applicable, using the + /// provided [`ReadOnlyNetworkGraph`]. + pub fn resolve(&mut self, network_graph: &ReadOnlyNetworkGraph) { + if let Destination::BlindedPath(path) = self { + if let IntroductionNode::DirectedShortChannelId(..) = path.introduction_node { + if let Some(pubkey) = path + .public_introduction_node_id(network_graph) + .and_then(|node_id| node_id.as_pubkey().ok()) + { + path.introduction_node = IntroductionNode::NodeId(pubkey); + } + } + } + } + pub(super) fn num_hops(&self) -> usize { match self { Destination::Node(_) => 1, @@ -441,13 +466,13 @@ impl Destination { } } - fn first_node(&self) -> PublicKey { + fn first_node(&self) -> Option { match self { - Destination::Node(node_id) => *node_id, + Destination::Node(node_id) => Some(*node_id), Destination::BlindedPath(BlindedPath { introduction_node, .. }) => { match introduction_node { - IntroductionNode::NodeId(pubkey) => *pubkey, - IntroductionNode::DirectedShortChannelId(..) => todo!(), + IntroductionNode::NodeId(pubkey) => Some(*pubkey), + IntroductionNode::DirectedShortChannelId(..) => None, } }, } @@ -492,6 +517,10 @@ pub enum SendError { /// /// [`NodeSigner`]: crate::sign::NodeSigner GetNodeIdFailed, + /// The provided [`Destination`] has a blinded path with an unresolved introduction node. An + /// attempt to resolve it in the [`MessageRouter`] when finding an [`OnionMessagePath`] likely + /// failed. + UnresolvedIntroductionNode, /// We attempted to send to a blinded path where we are the introduction node, and failed to /// advance the blinded path to make the second hop the new introduction node. Either /// [`NodeSigner::ecdh`] failed, we failed to tweak the current blinding point to get the @@ -548,18 +577,51 @@ pub enum PeeledOnion { Receive(ParsedOnionMessageContents, Option<[u8; 32]>, Option) } + +/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of +/// `path`, first calling [`Destination::resolve`] on `path.destination` with the given +/// [`ReadOnlyNetworkGraph`]. +/// +/// Returns the node id of the peer to send the message to, the message itself, and any addresses +/// needed to connect to the first node. +pub fn create_onion_message_resolving_destination< + ES: Deref, NS: Deref, NL: Deref, T: OnionMessageContents +>( + entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL, + network_graph: &ReadOnlyNetworkGraph, secp_ctx: &Secp256k1, + mut path: OnionMessagePath, contents: T, reply_path: Option, +) -> Result<(PublicKey, OnionMessage, Option>), SendError> +where + ES::Target: EntropySource, + NS::Target: NodeSigner, + NL::Target: NodeIdLookUp, +{ + path.destination.resolve(network_graph); + create_onion_message( + entropy_source, node_signer, node_id_lookup, secp_ctx, path, contents, reply_path, + ) +} + /// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of /// `path`. /// /// Returns the node id of the peer to send the message to, the message itself, and any addresses -/// need to connect to the first node. -pub fn create_onion_message( - entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, - path: OnionMessagePath, contents: T, reply_path: Option, +/// needed to connect to the first node. +/// +/// Returns [`SendError::UnresolvedIntroductionNode`] if: +/// - `destination` contains a blinded path with an [`IntroductionNode::DirectedShortChannelId`], +/// - unless it can be resolved by [`NodeIdLookUp::next_node_id`]. +/// Use [`create_onion_message_resolving_destination`] instead to resolve the introduction node +/// first with a [`ReadOnlyNetworkGraph`]. +pub fn create_onion_message( + entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL, + secp_ctx: &Secp256k1, path: OnionMessagePath, contents: T, + reply_path: Option, ) -> Result<(PublicKey, OnionMessage, Option>), SendError> where ES::Target: EntropySource, NS::Target: NodeSigner, + NL::Target: NodeIdLookUp, { let OnionMessagePath { intermediate_nodes, mut destination, first_node_addresses } = path; if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination { @@ -574,14 +636,19 @@ where // 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)?; let introduction_node_id = match blinded_path.introduction_node { IntroductionNode::NodeId(pubkey) => pubkey, - IntroductionNode::DirectedShortChannelId(..) => todo!(), + IntroductionNode::DirectedShortChannelId(direction, scid) => { + match node_id_lookup.next_node_id(scid) { + Some(next_node_id) => *direction.select_pubkey(&our_node_id, &next_node_id), + None => return Err(SendError::UnresolvedIntroductionNode), + } + }, }; - let our_node_id = node_signer.get_node_id(Recipient::Node) - .map_err(|()| SendError::GetNodeIdFailed)?; if introduction_node_id == our_node_id { - advance_path_by_one(blinded_path, node_signer, &secp_ctx) + advance_path_by_one(blinded_path, node_signer, node_id_lookup, &secp_ctx) .map_err(|()| SendError::BlindedPathAdvanceFailed)?; } } @@ -597,14 +664,16 @@ where Destination::BlindedPath(BlindedPath { introduction_node, blinding_point, .. }) => { match introduction_node { IntroductionNode::NodeId(pubkey) => (*pubkey, *blinding_point), - IntroductionNode::DirectedShortChannelId(..) => todo!(), + IntroductionNode::DirectedShortChannelId(..) => { + return Err(SendError::UnresolvedIntroductionNode); + }, } } } }; let (packet_payloads, packet_keys) = packet_payloads_and_keys( - &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret) - .map_err(|e| SendError::Secp256k1(e))?; + &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret + )?; let prng_seed = entropy_source.get_secure_random_bytes(); let onion_routing_packet = construct_onion_message_packet( @@ -711,12 +780,13 @@ where } } -impl -OnionMessenger +impl +OnionMessenger where ES::Target: EntropySource, NS::Target: NodeSigner, L::Target: Logger, + NL::Target: NodeIdLookUp, MR::Target: MessageRouter, OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, @@ -724,8 +794,8 @@ where /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to /// their respective handlers. pub fn new( - entropy_source: ES, node_signer: NS, logger: L, message_router: MR, offers_handler: OMH, - custom_handler: CMH + entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL, message_router: MR, + offers_handler: OMH, custom_handler: CMH ) -> Self { let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes()); @@ -735,6 +805,7 @@ where message_recipients: Mutex::new(new_hash_map()), secp_ctx, logger, + node_id_lookup, message_router, offers_handler, custom_handler, @@ -817,7 +888,8 @@ where log_trace!(self.logger, "Constructing onion message {}: {:?}", log_suffix, contents); let (first_node_id, onion_message, addresses) = create_onion_message( - &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path + &self.entropy_source, &self.node_signer, &self.node_id_lookup, &self.secp_ctx, path, + contents, reply_path, )?; let mut message_recipients = self.message_recipients.lock().unwrap(); @@ -913,12 +985,13 @@ fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap EventsProvider -for OnionMessenger +impl EventsProvider +for OnionMessenger where ES::Target: EntropySource, NS::Target: NodeSigner, L::Target: Logger, + NL::Target: NodeIdLookUp, MR::Target: MessageRouter, OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, @@ -934,12 +1007,13 @@ where } } -impl OnionMessageHandler -for OnionMessenger +impl OnionMessageHandler +for OnionMessenger where ES::Target: EntropySource, NS::Target: NodeSigner, L::Target: Logger, + NL::Target: NodeIdLookUp, MR::Target: MessageRouter, OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, @@ -977,7 +1051,13 @@ where Ok(PeeledOnion::Forward(next_hop, onion_message)) => { let next_node_id = match next_hop { NextHop::NodeId(pubkey) => pubkey, - NextHop::ShortChannelId(_) => todo!(), + NextHop::ShortChannelId(scid) => match self.node_id_lookup.next_node_id(scid) { + Some(pubkey) => pubkey, + None => { + log_trace!(self.logger, "Dropping forwarded onion messager: unable to resolve next hop using SCID {}", scid); + return + }, + }, }; let mut message_recipients = self.message_recipients.lock().unwrap(); @@ -1115,6 +1195,7 @@ pub type SimpleArcOnionMessenger = OnionMessenger< Arc, Arc, Arc, + Arc>, Arc>>, Arc, Arc>>, Arc>, IgnoringMessageHandler @@ -1134,8 +1215,9 @@ pub type SimpleRefOnionMessenger< &'a KeysManager, &'a KeysManager, &'b L, - &'i DefaultMessageRouter<&'g NetworkGraph<&'b L>, &'b L, &'a KeysManager>, - &'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>, + &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>, + &'j DefaultMessageRouter<&'g NetworkGraph<&'b L>, &'b L, &'a KeysManager>, + &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>, IgnoringMessageHandler >; @@ -1144,7 +1226,7 @@ pub type SimpleRefOnionMessenger< 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> { +) -> Result<(Vec<(Payload, [u8; 32])>, Vec), SendError> { let num_hops = unblinded_path.len() + destination.num_hops(); let mut payloads = Vec::with_capacity(num_hops); let mut onion_packet_keys = Vec::with_capacity(num_hops); @@ -1154,7 +1236,9 @@ fn packet_payloads_and_keys { let introduction_node_id = match introduction_node { IntroductionNode::NodeId(pubkey) => pubkey, - IntroductionNode::DirectedShortChannelId(..) => todo!(), + IntroductionNode::DirectedShortChannelId(..) => { + return Err(SendError::UnresolvedIntroductionNode); + }, }; (Some((*introduction_node_id, *blinding_point)), blinded_hops.len()) }, @@ -1206,7 +1290,7 @@ fn packet_payloads_and_keys