From: Jeffrey Czyz Date: Wed, 10 Apr 2024 21:04:19 +0000 (-0500) Subject: Compact a BlindedPath's introduction node X-Git-Tag: v0.0.124-beta~111^2~2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=0a6886ee38cfd906f984d7b8f444cb8621fce4fd;p=rust-lightning Compact a BlindedPath's introduction node Add a method to BlindedPath that given a network graph will compact the IntroductionNode as the DirectedShortChannelId variant. Call this method from DefaultMessageRouter so that Offer paths use the compact representation (along with reply paths). This leaves payment paths in Bolt12Invoice using the NodeId variant, as the compact representation isn't as useful there. --- diff --git a/lightning/src/blinded_path/mod.rs b/lightning/src/blinded_path/mod.rs index 5abf53ec1..2d3d085bd 100644 --- a/lightning/src/blinded_path/mod.rs +++ b/lightning/src/blinded_path/mod.rs @@ -21,6 +21,7 @@ use crate::offers::invoice::BlindedPayInfo; use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph}; use crate::sign::EntropySource; use crate::util::ser::{Readable, Writeable, Writer}; +use crate::util::scid_utils; use crate::io; use crate::prelude::*; @@ -217,6 +218,35 @@ impl BlindedPath { }, } } + + /// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed + /// short channel id from a channel in `network_graph` leading to the introduction node. + /// + /// While this may result in a smaller encoding, there is a trade off in that the path may + /// become invalid if the channel is closed or hasn't been propagated via gossip. Therefore, + /// calling this may not be suitable for long-lived blinded paths. + pub fn use_compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) { + if let IntroductionNode::NodeId(pubkey) = &self.introduction_node { + let node_id = NodeId::from_pubkey(pubkey); + if let Some(node_info) = network_graph.node(&node_id) { + if let Some((scid, channel_info)) = node_info + .channels + .iter() + .filter_map(|scid| network_graph.channel(*scid).map(|info| (*scid, info))) + .min_by_key(|(scid, _)| scid_utils::block_from_scid(*scid)) + { + let direction = if node_id == channel_info.node_one { + Direction::NodeOne + } else { + debug_assert_eq!(node_id, channel_info.node_two); + Direction::NodeTwo + }; + self.introduction_node = + IntroductionNode::DirectedShortChannelId(direction, scid); + } + } + } + } } impl Writeable for BlindedPath { diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 6484f5259..016afd32a 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -41,6 +41,7 @@ //! blinded paths are used. use bitcoin::network::constants::Network; +use bitcoin::secp256k1::PublicKey; use core::time::Duration; use crate::blinded_path::{BlindedPath, IntroductionNode}; use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext}; @@ -133,6 +134,12 @@ fn announce_node_address<'a, 'b, 'c>( } } +fn resolve_introduction_node<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, path: &BlindedPath) -> PublicKey { + path.public_introduction_node_id(&node.network_graph.read_only()) + .and_then(|node_id| node_id.as_pubkey().ok()) + .unwrap() +} + fn route_bolt12_payment<'a, 'b, 'c>( node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], invoice: &Bolt12Invoice ) { @@ -273,8 +280,9 @@ fn prefers_non_tor_nodes_in_blinded_paths() { assert_ne!(offer.signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { - assert_ne!(path.introduction_node, IntroductionNode::NodeId(bob_id)); - assert_ne!(path.introduction_node, IntroductionNode::NodeId(charlie_id)); + let introduction_node_id = resolve_introduction_node(david, &path); + assert_ne!(introduction_node_id, bob_id); + assert_ne!(introduction_node_id, charlie_id); } // Use a one-hop blinded path when Bob is announced and all his peers are Tor-only. @@ -288,7 +296,8 @@ fn prefers_non_tor_nodes_in_blinded_paths() { assert_ne!(offer.signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id)); + let introduction_node_id = resolve_introduction_node(david, &path); + assert_eq!(introduction_node_id, bob_id); } } @@ -338,7 +347,8 @@ fn prefers_more_connected_nodes_in_blinded_paths() { assert_ne!(offer.signing_pubkey(), Some(bob_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(nodes[4].node.get_our_node_id())); + let introduction_node_id = resolve_introduction_node(david, &path); + assert_eq!(introduction_node_id, nodes[4].node.get_our_node_id()); } } @@ -388,7 +398,9 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() { assert_ne!(offer.signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id)); + let introduction_node_id = resolve_introduction_node(david, &path); + assert_eq!(introduction_node_id, bob_id); + assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); } let payment_id = PaymentId([1; 32]); @@ -415,9 +427,11 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() { payer_note_truncated: None, }, }); + let introduction_node_id = resolve_introduction_node(alice, &reply_path); assert_eq!(invoice_request.amount_msats(), None); assert_ne!(invoice_request.payer_id(), david_id); - assert_eq!(reply_path.introduction_node, IntroductionNode::NodeId(charlie_id)); + assert_eq!(introduction_node_id, charlie_id); + assert!(matches!(reply_path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap(); charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message); @@ -489,7 +503,9 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() { assert_ne!(refund.payer_id(), david_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(charlie_id)); + let introduction_node_id = resolve_introduction_node(alice, &path); + assert_eq!(introduction_node_id, charlie_id); + assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); } expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id); @@ -545,7 +561,9 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() { assert_ne!(offer.signing_pubkey(), Some(alice_id)); assert!(!offer.paths().is_empty()); for path in offer.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id)); + let introduction_node_id = resolve_introduction_node(bob, &path); + assert_eq!(introduction_node_id, alice_id); + assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); } let payment_id = PaymentId([1; 32]); @@ -564,9 +582,11 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() { payer_note_truncated: None, }, }); + let introduction_node_id = resolve_introduction_node(alice, &reply_path); assert_eq!(invoice_request.amount_msats(), None); assert_ne!(invoice_request.payer_id(), bob_id); - assert_eq!(reply_path.introduction_node, IntroductionNode::NodeId(bob_id)); + assert_eq!(introduction_node_id, bob_id); + assert!(matches!(reply_path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); bob.onion_messenger.handle_onion_message(&alice_id, &onion_message); @@ -614,7 +634,9 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() { assert_ne!(refund.payer_id(), bob_id); assert!(!refund.paths().is_empty()); for path in refund.paths() { - assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id)); + let introduction_node_id = resolve_introduction_node(alice, &path); + assert_eq!(introduction_node_id, bob_id); + assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..))); } expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id); diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 5493b267e..fc3eead1e 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -445,7 +445,7 @@ where .take(MAX_PATHS) .collect::, _>>(); - match paths { + let mut paths = match paths { Ok(paths) if !paths.is_empty() => Ok(paths), _ => { if is_recipient_announced { @@ -455,7 +455,12 @@ where Err(()) } }, + }?; + for path in &mut paths { + path.use_compact_introduction_node(&network_graph); } + + Ok(paths) } }