]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Blinded payments to unannounced introduction node
authorJeffrey Czyz <jkczyz@gmail.com>
Wed, 12 Jun 2024 21:54:19 +0000 (16:54 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 18 Jun 2024 14:26:28 +0000 (09:26 -0500)
When creating blinded paths for receiving onion payments, allow using
the recipient's only peer as the introduction node when the recipient is
unannounced. This allows for sending payments without going through an
intermediary, which is useful for testing or when only connected to an
LSP with an empty NetworkGraph.

lightning/src/ln/offers_tests.rs
lightning/src/routing/router.rs

index 9f4f07df879ecf8e71865c7e1c2e9761a163b4b2..1922ef71be88d23ea2b6174d6e9a7e311ccdb3dd 100644 (file)
@@ -985,8 +985,18 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
        let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
        alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
 
-       let (_, reply_path) = extract_invoice_request(alice, &onion_message);
+       let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
+       assert_ne!(invoice_request.payer_id(), bob_id);
        assert_eq!(reply_path.introduction_node, IntroductionNode::NodeId(alice_id));
+
+       let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
+
+       let invoice = extract_invoice(bob, &onion_message);
+       assert_ne!(invoice.signing_pubkey(), alice_id);
+       assert!(!invoice.payment_paths().is_empty());
+       for (_, path) in invoice.payment_paths() {
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
+       }
 }
 
 /// Checks that a refund can be created using an unannounced node as a blinded path's introduction
@@ -1019,6 +1029,18 @@ fn creates_refund_with_blinded_path_using_unannounced_introduction_node() {
                assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
        }
        expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
+
+       let expected_invoice = alice.node.request_refund_payment(&refund).unwrap();
+
+       let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
+
+       let invoice = extract_invoice(bob, &onion_message);
+       assert_eq!(invoice, expected_invoice);
+       assert_ne!(invoice.signing_pubkey(), alice_id);
+       assert!(!invoice.payment_paths().is_empty());
+       for (_, path) in invoice.payment_paths() {
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
+       }
 }
 
 /// Fails creating or paying an offer when a blinded path cannot be created because no peers are
index 9a88d913896778dfc3b70ee0d97179c56e43c806..19931d5c5d8dadcd44196eb3e85bd6dd5b3030b4 100644 (file)
@@ -100,7 +100,20 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
                // recipient's node_id.
                const MIN_PEER_CHANNELS: usize = 3;
 
+               let has_one_peer = first_hops
+                       .first()
+                       .map(|details| details.counterparty.node_id)
+                       .map(|node_id| first_hops
+                               .iter()
+                               .skip(1)
+                               .all(|details| details.counterparty.node_id == node_id)
+                       )
+                       .unwrap_or(false);
+
                let network_graph = self.network_graph.deref().read_only();
+               let is_recipient_announced =
+                       network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient));
+
                let paths = first_hops.into_iter()
                        .filter(|details| details.counterparty.features.supports_route_blinding())
                        .filter(|details| amount_msats <= details.inbound_capacity_msat)
@@ -109,7 +122,8 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
                        .filter(|details| network_graph
                                        .node(&NodeId::from_pubkey(&details.counterparty.node_id))
                                        .map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
-                                       .unwrap_or(false)
+                                       // Allow payments directly with the only peer when unannounced.
+                                       .unwrap_or(!is_recipient_announced && has_one_peer)
                        )
                        .filter_map(|details| {
                                let short_channel_id = match details.get_inbound_payment_scid() {