@@ -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