@@ -368,7 +368,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
368
368
} else if chan. remote_network_id == * our_node_id {
369
369
return Err ( LightningError { err : "First hop cannot have our_node_id as a destination." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
370
370
}
371
- first_hop_targets. insert ( chan. remote_network_id , ( short_channel_id, chan. counterparty_features . clone ( ) ) ) ;
371
+ first_hop_targets. insert ( chan. remote_network_id , ( short_channel_id, chan. counterparty_features . clone ( ) , chan . outbound_capacity_msat ) ) ;
372
372
}
373
373
if first_hop_targets. is_empty ( ) {
374
374
return Err ( LightningError { err : "Cannot route when there are no outbound routes away from us" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
@@ -378,8 +378,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
378
378
// We don't want multiple paths (as per MPP) share liquidity of the same channels.
379
379
// This map allows paths to be aware of the channel use by other paths in the same call.
380
380
// This would help to make a better path finding decisions and not "overbook" channels.
381
- // It is unaware of the directions.
382
- // TODO: we could let a caller specify this. Definitely useful when considering our own channels.
381
+ // It is unaware of the directions (except for `outbound_capacity_msat` in `first_hops`).
383
382
let mut bookkeeped_channels_liquidity_available_msat = HashMap :: new ( ) ;
384
383
385
384
// Keeping track of how much value we already collected across other paths. Helps to decide:
@@ -576,8 +575,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
576
575
macro_rules! add_entries_to_cheapest_to_target_node {
577
576
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr ) => {
578
577
if first_hops. is_some( ) {
579
- if let Some ( & ( ref first_hop, ref features) ) = first_hop_targets. get( & $node_id) {
580
- 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) ;
578
+ if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat ) ) = first_hop_targets. get( & $node_id) {
579
+ 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) ;
581
580
}
582
581
}
583
582
@@ -646,7 +645,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
646
645
// it matters only if the fees are exactly the same.
647
646
for hop in last_hops. iter ( ) {
648
647
let have_hop_src_in_graph =
649
- if let Some ( & ( ref first_hop, ref features) ) = first_hop_targets. get ( & hop. src_node_id ) {
648
+ if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat ) ) = first_hop_targets. get ( & hop. src_node_id ) {
650
649
// If this hop connects to a node with which we have a direct channel, ignore
651
650
// the network graph and add both the hop and our direct channel to
652
651
// the candidate set.
@@ -655,7 +654,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
655
654
// bit lazy here. In the future, we should pull them out via our
656
655
// ChannelManager, but there's no reason to waste the space until we
657
656
// need them.
658
- add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, None :: < u64 > , features. to_context( ) , 0 , recommended_value_msat) ;
657
+ 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) ;
659
658
true
660
659
} else {
661
660
// In any other case, only add the hop if the source is in the regular network
@@ -701,7 +700,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
701
700
let mut ordered_hops = vec ! ( new_entry. clone( ) ) ;
702
701
703
702
' path_walk: loop {
704
- if let Some ( & ( _, ref features) ) = first_hop_targets. get ( & ordered_hops. last ( ) . unwrap ( ) . route_hop . pubkey ) {
703
+ if let Some ( & ( _, ref features, _ ) ) = first_hop_targets. get ( & ordered_hops. last ( ) . unwrap ( ) . route_hop . pubkey ) {
705
704
ordered_hops. last_mut ( ) . unwrap ( ) . route_hop . node_features = features. to_context ( ) ;
706
705
} else if let Some ( node) = network. get_nodes ( ) . get ( & ordered_hops. last ( ) . unwrap ( ) . route_hop . pubkey ) {
707
706
if let Some ( node_info) = node. announcement_info . as_ref ( ) {
@@ -1572,7 +1571,7 @@ mod tests {
1572
1571
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1573
1572
channel_value_satoshis: 0 ,
1574
1573
user_id: 0 ,
1575
- outbound_capacity_msat: 0 ,
1574
+ outbound_capacity_msat: 250_000_000 ,
1576
1575
inbound_capacity_msat: 0 ,
1577
1576
is_live: true ,
1578
1577
} ] ;
@@ -1619,7 +1618,7 @@ mod tests {
1619
1618
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1620
1619
channel_value_satoshis: 0 ,
1621
1620
user_id: 0 ,
1622
- outbound_capacity_msat: 0 ,
1621
+ outbound_capacity_msat: 250_000_000 ,
1623
1622
inbound_capacity_msat: 0 ,
1624
1623
is_live: true ,
1625
1624
} ] ;
@@ -1683,7 +1682,7 @@ mod tests {
1683
1682
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1684
1683
channel_value_satoshis: 0 ,
1685
1684
user_id: 0 ,
1686
- outbound_capacity_msat: 0 ,
1685
+ outbound_capacity_msat: 250_000_000 ,
1687
1686
inbound_capacity_msat: 0 ,
1688
1687
is_live: true ,
1689
1688
} ] ;
@@ -1819,7 +1818,7 @@ mod tests {
1819
1818
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
1820
1819
channel_value_satoshis: 0 ,
1821
1820
user_id: 0 ,
1822
- outbound_capacity_msat: 0 ,
1821
+ outbound_capacity_msat: 250_000_000 ,
1823
1822
inbound_capacity_msat: 0 ,
1824
1823
is_live: true ,
1825
1824
} ] ;
@@ -2053,6 +2052,66 @@ mod tests {
2053
2052
assert_eq ! ( path. last( ) . unwrap( ) . fee_msat, 250_000_000 ) ;
2054
2053
}
2055
2054
2055
+ // Check that setting outbound_capacity_msat in first_hops limits the channels.
2056
+ // Disable channel #1 and use another first hop.
2057
+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
2058
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
2059
+ short_channel_id : 1 ,
2060
+ timestamp : 3 ,
2061
+ flags : 2 ,
2062
+ cltv_expiry_delta : 0 ,
2063
+ htlc_minimum_msat : 0 ,
2064
+ htlc_maximum_msat : OptionalField :: Present ( 1_000_000_000 ) ,
2065
+ fee_base_msat : 0 ,
2066
+ fee_proportional_millionths : 0 ,
2067
+ excess_data : Vec :: new ( )
2068
+ } ) ;
2069
+
2070
+ // Now, limit the first_hop by the outbound_capacity_msat of 200_000 sats.
2071
+ let our_chans = vec ! [ channelmanager:: ChannelDetails {
2072
+ channel_id: [ 0 ; 32 ] ,
2073
+ short_channel_id: Some ( 42 ) ,
2074
+ remote_network_id: nodes[ 0 ] . clone( ) ,
2075
+ counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
2076
+ channel_value_satoshis: 0 ,
2077
+ user_id: 0 ,
2078
+ outbound_capacity_msat: 200_000_000 ,
2079
+ inbound_capacity_msat: 0 ,
2080
+ is_live: true ,
2081
+ } ] ;
2082
+
2083
+ {
2084
+ // Attempt to route more than available results in a failure.
2085
+ 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) ) {
2086
+ assert_eq ! ( err, "Failed to find a sufficient route to the given destination" ) ;
2087
+ } else { panic ! ( ) ; }
2088
+ }
2089
+
2090
+ {
2091
+ // Now, attempt to route an exact amount we have should be fine.
2092
+ 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 ( ) ;
2093
+ assert_eq ! ( route. paths. len( ) , 1 ) ;
2094
+ let path = route. paths . last ( ) . unwrap ( ) ;
2095
+ assert_eq ! ( path. len( ) , 2 ) ;
2096
+ assert_eq ! ( path. last( ) . unwrap( ) . pubkey, nodes[ 2 ] ) ;
2097
+ assert_eq ! ( path. last( ) . unwrap( ) . fee_msat, 200_000_000 ) ;
2098
+ }
2099
+
2100
+ // Enable channel #1 back.
2101
+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
2102
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
2103
+ short_channel_id : 1 ,
2104
+ timestamp : 4 ,
2105
+ flags : 0 ,
2106
+ cltv_expiry_delta : 0 ,
2107
+ htlc_minimum_msat : 0 ,
2108
+ htlc_maximum_msat : OptionalField :: Present ( 1_000_000_000 ) ,
2109
+ fee_base_msat : 0 ,
2110
+ fee_proportional_millionths : 0 ,
2111
+ excess_data : Vec :: new ( )
2112
+ } ) ;
2113
+
2114
+
2056
2115
// Now let's see if routing works if we know only htlc_maximum_msat.
2057
2116
update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 0 ] , UnsignedChannelUpdate {
2058
2117
chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
0 commit comments