Skip to content

Commit 7a1fc14

Browse files
committed
Use NodeCounters NodeIds as the blinded path intro references
The router's `introduction_node_id_cache` is used to cache the `NodeId`s of blinded path introduction points so that we don't have to look them up every time we go around the main router loop. When using it, if the introduction point isn't a public node we then look up the introduction in our first-hops map. In either case, we have to end up with a reference to a `NodeId` that outlives our `dist` map. Here we consolidate both the initial cache building and the first-hops map lookup to one place, storing only a reference to a `NodeId` either in the `NetworkGraph` or in the new `NodeCounters` to get the required lifetime without needing to reference into the first-hops map. We then take this opportunity to avoid `clone`ing the first-hops map entries as we now no longer reference into it.
1 parent bc4402d commit 7a1fc14

File tree

1 file changed

+66
-67
lines changed

1 file changed

+66
-67
lines changed

lightning/src/routing/router.rs

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,39 +1977,6 @@ where L::Target: Logger {
19771977
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
19781978
}
19791979

1980-
let introduction_node_id_cache = payment_params.payee.blinded_route_hints().iter()
1981-
.map(|(_, path)| path.public_introduction_node_id(network_graph))
1982-
.collect::<Vec<_>>();
1983-
match &payment_params.payee {
1984-
Payee::Clear { route_hints, node_id, .. } => {
1985-
for route in route_hints.iter() {
1986-
for hop in &route.0 {
1987-
if hop.src_node_id == *node_id {
1988-
return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
1989-
}
1990-
}
1991-
}
1992-
},
1993-
Payee::Blinded { route_hints, .. } => {
1994-
if introduction_node_id_cache.iter().all(|introduction_node_id| *introduction_node_id == Some(&our_node_id)) {
1995-
return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError});
1996-
}
1997-
for ((_, blinded_path), introduction_node_id) in route_hints.iter().zip(introduction_node_id_cache.iter()) {
1998-
if blinded_path.blinded_hops.len() == 0 {
1999-
return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError});
2000-
} else if *introduction_node_id == Some(&our_node_id) {
2001-
log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring");
2002-
} else if blinded_path.blinded_hops.len() == 1 &&
2003-
route_hints
2004-
.iter().zip(introduction_node_id_cache.iter())
2005-
.filter(|((_, p), _)| p.blinded_hops.len() == 1)
2006-
.any(|(_, p_introduction_node_id)| p_introduction_node_id != introduction_node_id)
2007-
{
2008-
return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), action: ErrorAction::IgnoreError});
2009-
}
2010-
}
2011-
}
2012-
}
20131980
let final_cltv_expiry_delta = payment_params.payee.final_cltv_expiry_delta().unwrap_or(0);
20141981
if payment_params.max_total_cltv_expiry_delta <= final_cltv_expiry_delta {
20151982
return Err(LightningError{err: "Can't find a route where the maximum total CLTV expiry delta is below the final CLTV expiry.".to_owned(), action: ErrorAction::IgnoreError});
@@ -2157,6 +2124,64 @@ where L::Target: Logger {
21572124

21582125
let node_counters = node_counters.build();
21592126

2127+
let introduction_node_id_cache = payment_params.payee.blinded_route_hints().iter()
2128+
.map(|(_, path)| {
2129+
match &path.introduction_node {
2130+
IntroductionNode::NodeId(pubkey) => {
2131+
node_counters.node_counter_from_id(&NodeId::from_pubkey(&pubkey))
2132+
},
2133+
IntroductionNode::DirectedShortChannelId(_, scid) => {
2134+
let node_id = if let Some(node_id) = path.public_introduction_node_id(network_graph) {
2135+
Some(node_id)
2136+
} else {
2137+
first_hop_targets.iter().find(|(_, channels)|
2138+
channels
2139+
.iter()
2140+
.any(|details| Some(*scid) == details.get_outbound_payment_scid())
2141+
).map(|(counterparty_node_id, _)| counterparty_node_id)
2142+
};
2143+
match node_id {
2144+
Some(node_id) => node_counters.node_counter_from_id(&node_id),
2145+
None => None,
2146+
}
2147+
},
2148+
}
2149+
})
2150+
.collect::<Vec<_>>();
2151+
match &payment_params.payee {
2152+
Payee::Clear { route_hints, node_id, .. } => {
2153+
for route in route_hints.iter() {
2154+
for hop in &route.0 {
2155+
if hop.src_node_id == *node_id {
2156+
return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
2157+
}
2158+
}
2159+
}
2160+
},
2161+
Payee::Blinded { route_hints, .. } => {
2162+
if introduction_node_id_cache.iter().all(|info_opt| info_opt.map(|(a, _)| a) == Some(&our_node_id)) {
2163+
return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError});
2164+
}
2165+
for ((_, blinded_path), info_opt) in route_hints.iter().zip(introduction_node_id_cache.iter()) {
2166+
if blinded_path.blinded_hops.len() == 0 {
2167+
return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError});
2168+
}
2169+
if info_opt.is_none() { continue }
2170+
let introduction_node_id = info_opt.unwrap().0;
2171+
if *introduction_node_id == our_node_id {
2172+
log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring");
2173+
} else if blinded_path.blinded_hops.len() == 1 &&
2174+
route_hints
2175+
.iter().zip(introduction_node_id_cache.iter())
2176+
.filter(|((_, p), _)| p.blinded_hops.len() == 1)
2177+
.any(|(_, p_introduction_node_id)| p_introduction_node_id != info_opt)
2178+
{
2179+
return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), action: ErrorAction::IgnoreError});
2180+
}
2181+
}
2182+
}
2183+
}
2184+
21602185
// The main heap containing all candidate next-hops sorted by their score (max(fee,
21612186
// htlc_minimum)). Ideally this would be a heap which allowed cheap score reduction instead of
21622187
// adding duplicate entries when we find a better path to a given node.
@@ -2658,31 +2683,8 @@ where L::Target: Logger {
26582683
// Only add the hops in this route to our candidate set if either
26592684
// we have a direct channel to the first hop or the first hop is
26602685
// in the regular network graph.
2661-
let source_node_id = match introduction_node_id_cache[hint_idx] {
2662-
Some(node_id) => node_id,
2663-
None => match &hint.1.introduction_node {
2664-
IntroductionNode::NodeId(pubkey) => {
2665-
let node_id = NodeId::from_pubkey(&pubkey);
2666-
match first_hop_targets.get_key_value(&node_id).map(|(key, _)| key) {
2667-
Some(node_id) => node_id,
2668-
None => continue,
2669-
}
2670-
},
2671-
IntroductionNode::DirectedShortChannelId(direction, scid) => {
2672-
let first_hop = first_hop_targets.iter().find(|(_, channels)|
2673-
channels
2674-
.iter()
2675-
.any(|details| Some(*scid) == details.get_outbound_payment_scid())
2676-
);
2677-
match first_hop {
2678-
Some((counterparty_node_id, _)) => {
2679-
direction.select_node_id(&our_node_id, counterparty_node_id)
2680-
},
2681-
None => continue,
2682-
}
2683-
},
2684-
},
2685-
};
2686+
let source_node_opt = introduction_node_id_cache[hint_idx];
2687+
let (source_node_id, _source_node_counter) = if let Some(v) = source_node_opt { v } else { continue };
26862688
if our_node_id == *source_node_id { continue }
26872689
let candidate = if hint.1.blinded_hops.len() == 1 {
26882690
CandidateRouteHop::OneHopBlinded(
@@ -2697,10 +2699,9 @@ where L::Target: Logger {
26972699
{
26982700
path_contribution_msat = hop_used_msat;
26992701
} else { continue }
2700-
if let Some(first_channels) = first_hop_targets.get(source_node_id) {
2701-
let mut first_channels = first_channels.clone();
2702+
if let Some(first_channels) = first_hop_targets.get_mut(source_node_id) {
27022703
sort_first_hop_channels(
2703-
&mut first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
2704+
first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
27042705
);
27052706
for details in first_channels {
27062707
let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
@@ -2798,10 +2799,9 @@ where L::Target: Logger {
27982799
.saturating_add(1);
27992800

28002801
// Searching for a direct channel between last checked hop and first_hop_targets
2801-
if let Some(first_channels) = first_hop_targets.get(target) {
2802-
let mut first_channels = first_channels.clone();
2802+
if let Some(first_channels) = first_hop_targets.get_mut(target) {
28032803
sort_first_hop_channels(
2804-
&mut first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
2804+
first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
28052805
);
28062806
for details in first_channels {
28072807
let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
@@ -2847,10 +2847,9 @@ where L::Target: Logger {
28472847
// Note that we *must* check if the last hop was added as `add_entry`
28482848
// always assumes that the third argument is a node to which we have a
28492849
// path.
2850-
if let Some(first_channels) = first_hop_targets.get(&NodeId::from_pubkey(&hop.src_node_id)) {
2851-
let mut first_channels = first_channels.clone();
2850+
if let Some(first_channels) = first_hop_targets.get_mut(&NodeId::from_pubkey(&hop.src_node_id)) {
28522851
sort_first_hop_channels(
2853-
&mut first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
2852+
first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
28542853
);
28552854
for details in first_channels {
28562855
let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {

0 commit comments

Comments
 (0)