Skip to content

Commit 5e9a998

Browse files
committed
Prefer well-connected nodes for introduction nodes
When forming blinded paths, using a reliable node as the introduction node is important to ensure onion message reliability. Order blinded paths by how well-connected the introduction node is as a proxy for reliability. For short paths (e.g., when the sender and recipient share an LSP), this may also result in a scenario where initiating a direct connection isn't necessary. That is helpful when using RGS since it currently doesn't include node announcements and thus cannot initiate a direct connection.
1 parent 7ab438d commit 5e9a998

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

lightning/src/ln/offers_tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,56 @@ fn prefers_non_tor_nodes_in_blinded_paths() {
263263
}
264264
}
265265

266+
/// Checks that blinded paths prefer an introduction node that is the most connected.
267+
#[test]
268+
fn prefers_more_connected_nodes_in_blinded_paths() {
269+
let mut accept_forward_cfg = test_default_channel_config();
270+
accept_forward_cfg.accept_forwards_to_priv_channels = true;
271+
272+
let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
273+
features.set_onion_messages_optional();
274+
features.set_route_blinding_optional();
275+
276+
let chanmon_cfgs = create_chanmon_cfgs(6);
277+
let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
278+
279+
*node_cfgs[1].override_init_features.borrow_mut() = Some(features);
280+
281+
let node_chanmgrs = create_node_chanmgrs(
282+
6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
283+
);
284+
let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
285+
286+
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
287+
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
288+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
289+
create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
290+
create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
291+
create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
292+
create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
293+
294+
// Add extra channels so that more than one of Bob's peers have MIN_PEER_CHANNELS and one has
295+
// more than the others.
296+
create_announced_chan_between_nodes_with_value(&nodes, 0, 4, 10_000_000, 1_000_000_000);
297+
create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 10_000_000, 1_000_000_000);
298+
299+
let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
300+
let bob_id = bob.node.get_our_node_id();
301+
302+
disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
303+
disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
304+
305+
let offer = bob.node
306+
.create_offer_builder("coffee".to_string()).unwrap()
307+
.amount_msats(10_000_000)
308+
.build().unwrap();
309+
assert_ne!(offer.signing_pubkey(), bob_id);
310+
assert!(!offer.paths().is_empty());
311+
for path in offer.paths() {
312+
assert_eq!(path.introduction_node_id, nodes[4].node.get_our_node_id());
313+
}
314+
}
315+
266316
/// Checks that an offer can be paid through blinded paths and that ephemeral pubkeys are used
267317
/// rather than exposing a node's pubkey.
268318
#[test]

lightning/src/onion_message/messenger.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,17 @@ where
364364
network_graph
365365
.node(&NodeId::from_pubkey(pubkey))
366366
.filter(|info| info.channels.len() >= MIN_PEER_CHANNELS)
367-
.map(|info| (*pubkey, info.is_tor_only()))
367+
.map(|info| (*pubkey, info.is_tor_only(), info.channels.len()))
368368
)
369369
.collect::<Vec<_>>();
370-
peer_info.sort_unstable_by(|(_, a_tor_only), (_, b_tor_only)| a_tor_only.cmp(b_tor_only));
370+
371+
// Prefer using non-Tor nodes with the most channels as the introduction node.
372+
peer_info.sort_unstable_by(|(_, a_tor_only, a_channels), (_, b_tor_only, b_channels)| {
373+
a_tor_only.cmp(b_tor_only).then(a_channels.cmp(b_channels).reverse())
374+
});
371375

372376
let paths = peer_info.into_iter()
373-
.map(|(pubkey, _)| vec![pubkey, recipient])
377+
.map(|(pubkey, _, _)| vec![pubkey, recipient])
374378
.map(|node_pks| BlindedPath::new_for_message(&node_pks, &*self.entropy_source, secp_ctx))
375379
.take(MAX_PATHS)
376380
.collect::<Result<Vec<_>, _>>();

0 commit comments

Comments
 (0)