@@ -376,7 +376,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
376
376
} else if chan. remote_network_id == * our_node_id {
377
377
return Err ( LightningError { err : "First hop cannot have our_node_id as a destination." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
378
378
}
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 ) ) ;
380
380
}
381
381
if first_hop_targets. is_empty ( ) {
382
382
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
386
386
// We don't want multiple paths (as per MPP) share liquidity of the same channels.
387
387
// This map allows paths to be aware of the channel use by other paths in the same call.
388
388
// 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`).
391
390
let mut bookkeeped_channels_liquidity_available_msat = HashMap :: new ( ) ;
392
391
393
392
// 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
584
583
macro_rules! add_entries_to_cheapest_to_target_node {
585
584
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr ) => {
586
585
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) ;
589
588
}
590
589
}
591
590
@@ -654,7 +653,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
654
653
// it matters only if the fees are exactly the same.
655
654
for hop in last_hops. iter ( ) {
656
655
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 ) {
658
657
// If this hop connects to a node with which we have a direct channel, ignore
659
658
// the network graph and add both the hop and our direct channel to
660
659
// the candidate set.
@@ -663,7 +662,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
663
662
// bit lazy here. In the future, we should pull them out via our
664
663
// ChannelManager, but there's no reason to waste the space until we
665
664
// 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) ;
667
666
true
668
667
} else {
669
668
// 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
709
708
let mut ordered_hops = vec ! ( new_entry. clone( ) ) ;
710
709
711
710
' 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 ) {
713
712
ordered_hops. last_mut ( ) . unwrap ( ) . route_hop . node_features = features. to_context ( ) ;
714
713
} else if let Some ( node) = network. get_nodes ( ) . get ( & ordered_hops. last ( ) . unwrap ( ) . route_hop . pubkey ) {
715
714
if let Some ( node_info) = node. announcement_info . as_ref ( ) {
@@ -1585,7 +1584,7 @@ mod tests {
1585
1584
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1586
1585
channel_value_satoshis: 0 ,
1587
1586
user_id: 0 ,
1588
- outbound_capacity_msat: 0 ,
1587
+ outbound_capacity_msat: 250_000_000 ,
1589
1588
inbound_capacity_msat: 0 ,
1590
1589
is_live: true ,
1591
1590
} ] ;
@@ -1632,7 +1631,7 @@ mod tests {
1632
1631
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1633
1632
channel_value_satoshis: 0 ,
1634
1633
user_id: 0 ,
1635
- outbound_capacity_msat: 0 ,
1634
+ outbound_capacity_msat: 250_000_000 ,
1636
1635
inbound_capacity_msat: 0 ,
1637
1636
is_live: true ,
1638
1637
} ] ;
@@ -1696,7 +1695,7 @@ mod tests {
1696
1695
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1697
1696
channel_value_satoshis: 0 ,
1698
1697
user_id: 0 ,
1699
- outbound_capacity_msat: 0 ,
1698
+ outbound_capacity_msat: 250_000_000 ,
1700
1699
inbound_capacity_msat: 0 ,
1701
1700
is_live: true ,
1702
1701
} ] ;
@@ -1832,7 +1831,7 @@ mod tests {
1832
1831
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1833
1832
channel_value_satoshis: 0 ,
1834
1833
user_id: 0 ,
1835
- outbound_capacity_msat: 0 ,
1834
+ outbound_capacity_msat: 250_000_000 ,
1836
1835
inbound_capacity_msat: 0 ,
1837
1836
is_live: true ,
1838
1837
} ] ;
@@ -2066,6 +2065,66 @@ mod tests {
2066
2065
assert_eq ! ( path. last( ) . unwrap( ) . fee_msat, 250_000_000 ) ;
2067
2066
}
2068
2067
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
+
2069
2128
// Now let's see if routing works if we know only htlc_maximum_msat.
2070
2129
update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 0 ] , UnsignedChannelUpdate {
2071
2130
chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
0 commit comments