Skip to content

Commit 2110da2

Browse files
committed
Use outbound_capacity_msat from first_hops for routing
1 parent 811a3d7 commit 2110da2

File tree

1 file changed

+71
-12
lines changed

1 file changed

+71
-12
lines changed

lightning/src/routing/router.rs

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
376376
} else if chan.remote_network_id == *our_node_id {
377377
return Err(LightningError{err: "First hop cannot have our_node_id as a destination.".to_owned(), action: ErrorAction::IgnoreError});
378378
}
379-
first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone()));
379+
first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone(), chan.outbound_capacity_msat));
380380
}
381381
if first_hop_targets.is_empty() {
382382
return Err(LightningError{err: "Cannot route when there are no outbound routes away from us".to_owned(), action: ErrorAction::IgnoreError});
@@ -386,8 +386,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
386386
// We don't want multiple paths (as per MPP) share liquidity of the same channels.
387387
// This map allows paths to be aware of the channel use by other paths in the same call.
388388
// This would help to make a better path finding decisions and not "overbook" channels.
389-
// It is unaware of the directions.
390-
// TODO: we could let a caller specify this. Definitely useful when considering our own channels.
389+
// It is unaware of the directions (except for `outbound_capacity_msat` in `first_hops`).
391390
let mut bookkeeped_channels_liquidity_available_msat = HashMap::new();
392391

393392
// Keeping track of how much value we already collected across other paths. Helps to decide:
@@ -584,8 +583,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
584583
macro_rules! add_entries_to_cheapest_to_target_node {
585584
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr ) => {
586585
if first_hops.is_some() {
587-
if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&$node_id) {
588-
add_entry!(first_hop, *our_node_id, $node_id, dummy_directional_info, None::<u64>, features.to_context(), $fee_to_target_msat, $next_hops_value_contribution);
586+
if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat)) = first_hop_targets.get(&$node_id) {
587+
add_entry!(first_hop, *our_node_id, $node_id, dummy_directional_info, Some(outbound_capacity_msat / 1000), features.to_context(), $fee_to_target_msat, $next_hops_value_contribution);
589588
}
590589
}
591590

@@ -654,7 +653,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
654653
// it matters only if the fees are exactly the same.
655654
for hop in last_hops.iter() {
656655
let have_hop_src_in_graph =
657-
if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&hop.src_node_id) {
656+
if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat)) = first_hop_targets.get(&hop.src_node_id) {
658657
// If this hop connects to a node with which we have a direct channel, ignore
659658
// the network graph and add both the hop and our direct channel to
660659
// the candidate set.
@@ -663,7 +662,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
663662
// bit lazy here. In the future, we should pull them out via our
664663
// ChannelManager, but there's no reason to waste the space until we
665664
// need them.
666-
add_entry!(first_hop, *our_node_id , hop.src_node_id, dummy_directional_info, None::<u64>, features.to_context(), 0, recommended_value_msat);
665+
add_entry!(first_hop, *our_node_id , hop.src_node_id, dummy_directional_info, Some(outbound_capacity_msat / 1000), features.to_context(), 0, recommended_value_msat);
667666
true
668667
} else {
669668
// In any other case, only add the hop if the source is in the regular network
@@ -709,7 +708,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
709708
let mut ordered_hops = vec!(new_entry.clone());
710709

711710
'path_walk: loop {
712-
if let Some(&(_, ref features)) = first_hop_targets.get(&ordered_hops.last().unwrap().route_hop.pubkey) {
711+
if let Some(&(_, ref features, _)) = first_hop_targets.get(&ordered_hops.last().unwrap().route_hop.pubkey) {
713712
ordered_hops.last_mut().unwrap().route_hop.node_features = features.to_context();
714713
} else if let Some(node) = network.get_nodes().get(&ordered_hops.last().unwrap().route_hop.pubkey) {
715714
if let Some(node_info) = node.announcement_info.as_ref() {
@@ -1585,7 +1584,7 @@ mod tests {
15851584
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
15861585
channel_value_satoshis: 0,
15871586
user_id: 0,
1588-
outbound_capacity_msat: 0,
1587+
outbound_capacity_msat: 250_000_000,
15891588
inbound_capacity_msat: 0,
15901589
is_live: true,
15911590
}];
@@ -1632,7 +1631,7 @@ mod tests {
16321631
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
16331632
channel_value_satoshis: 0,
16341633
user_id: 0,
1635-
outbound_capacity_msat: 0,
1634+
outbound_capacity_msat: 250_000_000,
16361635
inbound_capacity_msat: 0,
16371636
is_live: true,
16381637
}];
@@ -1696,7 +1695,7 @@ mod tests {
16961695
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
16971696
channel_value_satoshis: 0,
16981697
user_id: 0,
1699-
outbound_capacity_msat: 0,
1698+
outbound_capacity_msat: 250_000_000,
17001699
inbound_capacity_msat: 0,
17011700
is_live: true,
17021701
}];
@@ -1832,7 +1831,7 @@ mod tests {
18321831
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
18331832
channel_value_satoshis: 0,
18341833
user_id: 0,
1835-
outbound_capacity_msat: 0,
1834+
outbound_capacity_msat: 250_000_000,
18361835
inbound_capacity_msat: 0,
18371836
is_live: true,
18381837
}];
@@ -2066,6 +2065,66 @@ mod tests {
20662065
assert_eq!(path.last().unwrap().fee_msat, 250_000_000);
20672066
}
20682067

2068+
// Check that setting outbound_capacity_msat in first_hops limits the channels.
2069+
// Disable channel #1 and use another first hop.
2070+
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
2071+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
2072+
short_channel_id: 1,
2073+
timestamp: 3,
2074+
flags: 2,
2075+
cltv_expiry_delta: 0,
2076+
htlc_minimum_msat: 0,
2077+
htlc_maximum_msat: OptionalField::Present(1_000_000_000),
2078+
fee_base_msat: 0,
2079+
fee_proportional_millionths: 0,
2080+
excess_data: Vec::new()
2081+
});
2082+
2083+
// Now, limit the first_hop by the outbound_capacity_msat of 200_000 sats.
2084+
let our_chans = vec![channelmanager::ChannelDetails {
2085+
channel_id: [0; 32],
2086+
short_channel_id: Some(42),
2087+
remote_network_id: nodes[0].clone(),
2088+
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
2089+
channel_value_satoshis: 0,
2090+
user_id: 0,
2091+
outbound_capacity_msat: 200_000_000,
2092+
inbound_capacity_msat: 0,
2093+
is_live: true,
2094+
}];
2095+
2096+
{
2097+
// Attempt to route more than available results in a failure.
2098+
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_001, 42, Arc::clone(&logger)) {
2099+
assert_eq!(err, "Failed to find a sufficient route to the given destination");
2100+
} else { panic!(); }
2101+
}
2102+
2103+
{
2104+
// Now, attempt to route an exact amount we have should be fine.
2105+
let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_000, 42, Arc::clone(&logger)).unwrap();
2106+
assert_eq!(route.paths.len(), 1);
2107+
let path = route.paths.last().unwrap();
2108+
assert_eq!(path.len(), 2);
2109+
assert_eq!(path.last().unwrap().pubkey, nodes[2]);
2110+
assert_eq!(path.last().unwrap().fee_msat, 200_000_000);
2111+
}
2112+
2113+
// Enable channel #1 back.
2114+
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
2115+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
2116+
short_channel_id: 1,
2117+
timestamp: 4,
2118+
flags: 0,
2119+
cltv_expiry_delta: 0,
2120+
htlc_minimum_msat: 0,
2121+
htlc_maximum_msat: OptionalField::Present(1_000_000_000),
2122+
fee_base_msat: 0,
2123+
fee_proportional_millionths: 0,
2124+
excess_data: Vec::new()
2125+
});
2126+
2127+
20692128
// Now let's see if routing works if we know only htlc_maximum_msat.
20702129
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
20712130
chain_hash: genesis_block(Network::Testnet).header.block_hash(),

0 commit comments

Comments
 (0)