]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Compact a BlindedPath's introduction node
authorJeffrey Czyz <jkczyz@gmail.com>
Wed, 10 Apr 2024 21:04:19 +0000 (16:04 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 28 May 2024 21:35:56 +0000 (16:35 -0500)
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.

lightning/src/blinded_path/mod.rs
lightning/src/ln/offers_tests.rs
lightning/src/onion_message/messenger.rs

index 5abf53ec166561b351c17255eeaf7424cf2677fb..2d3d085bddf35d51a5276c5c9c048506527ac45b 100644 (file)
@@ -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 {
index 6484f5259f6dd99b9eb0025801cfe4b773ae5c79..016afd32a09c209c8c2b81eb2ef2def3f3205417 100644 (file)
@@ -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);
 
index 5493b267ed9527e8b0b6de2db0b7ee532ed7cb20..fc3eead1e4fac6a53e16f165ea39e7ff2f129710 100644 (file)
@@ -445,7 +445,7 @@ where
                        .take(MAX_PATHS)
                        .collect::<Result<Vec<_>, _>>();
 
-               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)
        }
 }