@@ -380,7 +380,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
380
380
} else if chan.remote_network_id == *our_node_id {
381
381
return Err(LightningError{err: "First hop cannot have our_node_id as a destination.".to_owned(), action: ErrorAction::IgnoreError});
382
382
}
383
- first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone()));
383
+ first_hop_targets.insert(chan.remote_network_id, (short_channel_id, chan.counterparty_features.clone(), chan.outbound_capacity_msat ));
384
384
}
385
385
if first_hop_targets.is_empty() {
386
386
return Err(LightningError{err: "Cannot route when there are no outbound routes away from us".to_owned(), action: ErrorAction::IgnoreError});
@@ -390,8 +390,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
390
390
// We don't want multiple paths (as per MPP) share liquidity of the same channels.
391
391
// This map allows paths to be aware of the channel use by other paths in the same call.
392
392
// This would help to make a better path finding decisions and not "overbook" channels.
393
- // It is unaware of the directions.
394
- // TODO: we could let a caller specify this. Definitely useful when considering our own channels.
393
+ // It is unaware of the directions (except for `outbound_capacity_msat` in `first_hops`).
395
394
let mut bookkeeped_channels_liquidity_available_msat = HashMap::new();
396
395
397
396
// Keeping track of how much value we already collected across other paths. Helps to decide:
@@ -588,8 +587,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
588
587
macro_rules! add_entries_to_cheapest_to_target_node {
589
588
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr ) => {
590
589
if first_hops.is_some() {
591
- if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&$node_id) {
592
- 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);
590
+ if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat )) = first_hop_targets.get(&$node_id) {
591
+ 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);
593
592
}
594
593
}
595
594
@@ -658,7 +657,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
658
657
// it matters only if the fees are exactly the same.
659
658
for hop in last_hops.iter() {
660
659
let have_hop_src_in_graph =
661
- if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&hop.src_node_id) {
660
+ if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat )) = first_hop_targets.get(&hop.src_node_id) {
662
661
// If this hop connects to a node with which we have a direct channel, ignore
663
662
// the network graph and add both the hop and our direct channel to
664
663
// the candidate set.
@@ -667,7 +666,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
667
666
// bit lazy here. In the future, we should pull them out via our
668
667
// ChannelManager, but there's no reason to waste the space until we
669
668
// need them.
670
- add_entry!(first_hop, *our_node_id , hop.src_node_id, dummy_directional_info, None::<u64> , features.to_context(), 0, recommended_value_msat);
669
+ 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);
671
670
true
672
671
} else {
673
672
// In any other case, only add the hop if the source is in the regular network
@@ -713,7 +712,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
713
712
let mut ordered_hops = vec!(new_entry.clone());
714
713
715
714
'path_walk: loop {
716
- if let Some(&(_, ref features)) = first_hop_targets.get(&ordered_hops.last().unwrap().route_hop.pubkey) {
715
+ if let Some(&(_, ref features, _ )) = first_hop_targets.get(&ordered_hops.last().unwrap().route_hop.pubkey) {
717
716
ordered_hops.last_mut().unwrap().route_hop.node_features = features.to_context();
718
717
} else if let Some(node) = network.get_nodes().get(&ordered_hops.last().unwrap().route_hop.pubkey) {
719
718
if let Some(node_info) = node.announcement_info.as_ref() {
@@ -1594,7 +1593,7 @@ mod tests {
1594
1593
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
1595
1594
channel_value_satoshis: 0,
1596
1595
user_id: 0,
1597
- outbound_capacity_msat: 0 ,
1596
+ outbound_capacity_msat: 250_000_000 ,
1598
1597
inbound_capacity_msat: 0,
1599
1598
is_live: true,
1600
1599
}];
@@ -1641,7 +1640,7 @@ mod tests {
1641
1640
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
1642
1641
channel_value_satoshis: 0,
1643
1642
user_id: 0,
1644
- outbound_capacity_msat: 0 ,
1643
+ outbound_capacity_msat: 250_000_000 ,
1645
1644
inbound_capacity_msat: 0,
1646
1645
is_live: true,
1647
1646
}];
@@ -1705,7 +1704,7 @@ mod tests {
1705
1704
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
1706
1705
channel_value_satoshis: 0,
1707
1706
user_id: 0,
1708
- outbound_capacity_msat: 0 ,
1707
+ outbound_capacity_msat: 250_000_000 ,
1709
1708
inbound_capacity_msat: 0,
1710
1709
is_live: true,
1711
1710
}];
@@ -1841,7 +1840,7 @@ mod tests {
1841
1840
counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
1842
1841
channel_value_satoshis: 0,
1843
1842
user_id: 0,
1844
- outbound_capacity_msat: 0 ,
1843
+ outbound_capacity_msat: 250_000_000 ,
1845
1844
inbound_capacity_msat: 0,
1846
1845
is_live: true,
1847
1846
}];
@@ -2075,6 +2074,66 @@ mod tests {
2075
2074
assert_eq!(path.last().unwrap().fee_msat, 250_000_000);
2076
2075
}
2077
2076
2077
+ // Check that setting outbound_capacity_msat in first_hops limits the channels.
2078
+ // Disable channel #1 and use another first hop.
2079
+ update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
2080
+ chain_hash: genesis_block(Network::Testnet).header.block_hash(),
2081
+ short_channel_id: 1,
2082
+ timestamp: 3,
2083
+ flags: 2,
2084
+ cltv_expiry_delta: 0,
2085
+ htlc_minimum_msat: 0,
2086
+ htlc_maximum_msat: OptionalField::Present(1_000_000_000),
2087
+ fee_base_msat: 0,
2088
+ fee_proportional_millionths: 0,
2089
+ excess_data: Vec::new()
2090
+ });
2091
+
2092
+ // Now, limit the first_hop by the outbound_capacity_msat of 200_000 sats.
2093
+ let our_chans = vec![channelmanager::ChannelDetails {
2094
+ channel_id: [0; 32],
2095
+ short_channel_id: Some(42),
2096
+ remote_network_id: nodes[0].clone(),
2097
+ counterparty_features: InitFeatures::from_le_bytes(vec![0b11]),
2098
+ channel_value_satoshis: 0,
2099
+ user_id: 0,
2100
+ outbound_capacity_msat: 200_000_000,
2101
+ inbound_capacity_msat: 0,
2102
+ is_live: true,
2103
+ }];
2104
+
2105
+ {
2106
+ // Attempt to route more than available results in a failure.
2107
+ 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)) {
2108
+ assert_eq!(err, "Failed to find a sufficient route to the given destination");
2109
+ } else { panic!(); }
2110
+ }
2111
+
2112
+ {
2113
+ // Now, attempt to route an exact amount we have should be fine.
2114
+ 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();
2115
+ assert_eq!(route.paths.len(), 1);
2116
+ let path = route.paths.last().unwrap();
2117
+ assert_eq!(path.len(), 2);
2118
+ assert_eq!(path.last().unwrap().pubkey, nodes[2]);
2119
+ assert_eq!(path.last().unwrap().fee_msat, 200_000_000);
2120
+ }
2121
+
2122
+ // Enable channel #1 back.
2123
+ update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
2124
+ chain_hash: genesis_block(Network::Testnet).header.block_hash(),
2125
+ short_channel_id: 1,
2126
+ timestamp: 4,
2127
+ flags: 0,
2128
+ cltv_expiry_delta: 0,
2129
+ htlc_minimum_msat: 0,
2130
+ htlc_maximum_msat: OptionalField::Present(1_000_000_000),
2131
+ fee_base_msat: 0,
2132
+ fee_proportional_millionths: 0,
2133
+ excess_data: Vec::new()
2134
+ });
2135
+
2136
+
2078
2137
// Now let's see if routing works if we know only htlc_maximum_msat.
2079
2138
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
2080
2139
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
0 commit comments