@@ -513,8 +513,11 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
513513 // $directional_info.
514514 // $next_hops_fee_msat represents the fees paid for using all the channel *after* this one,
515515 // since that value has to be transferred over this channel.
516+ // Returns whether this channel caused an update to `targets`.
516517 ( $chan_id: expr, $src_node_id: expr, $dest_node_id: expr, $directional_info: expr, $capacity_sats: expr, $chan_features: expr, $next_hops_fee_msat: expr,
517- $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr ) => {
518+ $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr ) => { {
519+ // We "return" whether we updated the path at the end, via this:
520+ let mut did_add_update_path_to_src_node = false ;
518521 // Channels to self should not be used. This is more of belt-and-suspenders, because in
519522 // practice these cases should be caught earlier:
520523 // - for regular channels at channel announcement (TODO)
@@ -726,6 +729,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
726729 {
727730 old_entry. value_contribution_msat = value_contribution_msat;
728731 }
732+ did_add_update_path_to_src_node = true ;
729733 } else if old_entry. was_processed && new_cost < old_cost {
730734 #[ cfg( any( test, feature = "fuzztarget" ) ) ]
731735 {
@@ -756,7 +760,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
756760 }
757761 }
758762 }
759- } ;
763+ did_add_update_path_to_src_node
764+ } }
760765 }
761766
762767 let empty_node_features = NodeFeatures :: empty ( ) ;
@@ -859,22 +864,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
859864 // it matters only if the fees are exactly the same.
860865 for hop in last_hops. iter ( ) {
861866 let have_hop_src_in_graph =
862- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & hop. src_node_id ) {
863- // If this hop connects to a node with which we have a direct channel, ignore
864- // the network graph and add both the hop and our direct channel to
865- // the candidate set.
866- //
867- // Currently there are no channel-context features defined, so we are a
868- // bit lazy here. In the future, we should pull them out via our
869- // ChannelManager, but there's no reason to waste the space until we
870- // need them.
871- add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, 0 , path_value_msat, 0 ) ;
872- true
873- } else {
874- // In any other case, only add the hop if the source is in the regular network
875- // graph:
876- network. get_nodes ( ) . get ( & hop. src_node_id ) . is_some ( )
877- } ;
867+ // Only add the last hop to our candidate set if either we have a direct channel or
868+ // they are in the regular network graph.
869+ first_hop_targets. get ( & hop. src_node_id ) . is_some ( ) ||
870+ network. get_nodes ( ) . get ( & hop. src_node_id ) . is_some ( ) ;
878871 if have_hop_src_in_graph {
879872 // BOLT 11 doesn't allow inclusion of features for the last hop hints, which
880873 // really sucks, cause we're gonna need that eventually.
@@ -888,7 +881,18 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
888881 htlc_maximum_msat : hop. htlc_maximum_msat ,
889882 fees : hop. fees ,
890883 } ;
891- add_entry ! ( hop. short_channel_id, hop. src_node_id, payee, directional_info, None :: <u64 >, & empty_channel_features, 0 , path_value_msat, 0 ) ;
884+ if add_entry ! ( hop. short_channel_id, hop. src_node_id, payee, directional_info, None :: <u64 >, & empty_channel_features, 0 , path_value_msat, 0 ) {
885+ // If this hop connects to a node with which we have a direct channel,
886+ // ignore the network graph and, if the last hop was added, add our
887+ // direct channel to the candidate set.
888+ //
889+ // Note that we *must* check if the last hop was added as `add_entry`
890+ // always assumes that the third argument is a node to which we have a
891+ // path.
892+ if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & hop. src_node_id ) {
893+ add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, 0 , path_value_msat, 0 ) ;
894+ }
895+ }
892896 }
893897 }
894898
@@ -1159,7 +1163,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11591163
11601164#[ cfg( test) ]
11611165mod tests {
1162- use routing:: router:: { get_route, RouteHint , RouteHintHop , RoutingFees } ;
1166+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
11631167 use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
11641168 use chain:: transaction:: OutPoint ;
11651169 use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -2307,11 +2311,7 @@ mod tests {
23072311 assert_eq ! ( route. paths[ 0 ] [ 4 ] . channel_features. le_flags( ) , & Vec :: <u8 >:: new( ) ) ; // We can't learn any flags from invoices, sadly
23082312 }
23092313
2310- #[ test]
2311- fn unannounced_path_test ( ) {
2312- // We should be able to send a payment to a destination without any help of a routing graph
2313- // if we have a channel with a common counterparty that appears in the first and last hop
2314- // hints.
2314+ fn do_unannounced_path_test ( last_hop_htlc_max : Option < u64 > , last_hop_fee_prop : u32 , outbound_capacity_msat : u64 , route_val : u64 ) -> Result < Route , LightningError > {
23152315 let source_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 41 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23162316 let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23172317 let target_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 43 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
@@ -2322,11 +2322,11 @@ mod tests {
23222322 short_channel_id: 8 ,
23232323 fees: RoutingFees {
23242324 base_msat: 1000 ,
2325- proportional_millionths: 0 ,
2325+ proportional_millionths: last_hop_fee_prop ,
23262326 } ,
23272327 cltv_expiry_delta: ( 8 << 8 ) | 1 ,
23282328 htlc_minimum_msat: None ,
2329- htlc_maximum_msat: None ,
2329+ htlc_maximum_msat: last_hop_htlc_max ,
23302330 } ] ) ;
23312331 let our_chans = vec ! [ channelmanager:: ChannelDetails {
23322332 channel_id: [ 0 ; 32 ] ,
@@ -2336,31 +2336,60 @@ mod tests {
23362336 counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
23372337 channel_value_satoshis: 100000 ,
23382338 user_id: 0 ,
2339- outbound_capacity_msat: 100000 ,
2339+ outbound_capacity_msat: outbound_capacity_msat ,
23402340 inbound_capacity_msat: 100000 ,
23412341 is_outbound: true , is_funding_locked: true ,
23422342 is_usable: true , is_public: true ,
23432343 counterparty_forwarding_info: None ,
23442344 } ] ;
2345- let route = get_route ( & source_node_id, & NetworkGraph :: new ( genesis_block ( Network :: Testnet ) . header . block_hash ( ) ) , & target_node_id, None , Some ( & our_chans. iter ( ) . collect :: < Vec < _ > > ( ) ) , & vec ! [ & last_hops] , 100 , 42 , Arc :: new ( test_utils:: TestLogger :: new ( ) ) ) . unwrap ( ) ;
2345+ get_route ( & source_node_id, & NetworkGraph :: new ( genesis_block ( Network :: Testnet ) . header . block_hash ( ) ) , & target_node_id, None , Some ( & our_chans. iter ( ) . collect :: < Vec < _ > > ( ) ) , & vec ! [ & last_hops] , route_val, 42 , Arc :: new ( test_utils:: TestLogger :: new ( ) ) )
2346+ }
2347+ #[ test]
2348+ fn unannounced_path_test ( ) {
2349+ // We should be able to send a payment to a destination without any help of a routing graph
2350+ // if we have a channel with a common counterparty that appears in the first and last hop
2351+ // hints.
2352+ let route = do_unannounced_path_test ( None , 1 , 2000000 , 1000000 ) . unwrap ( ) ;
23462353
2354+ let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2355+ let target_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 43 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23472356 assert_eq ! ( route. paths[ 0 ] . len( ) , 2 ) ;
23482357
23492358 assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, middle_node_id) ;
23502359 assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 42 ) ;
2351- assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1000 ) ;
2360+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1001 ) ;
23522361 assert_eq ! ( route. paths[ 0 ] [ 0 ] . cltv_expiry_delta, ( 8 << 8 ) | 1 ) ;
23532362 assert_eq ! ( route. paths[ 0 ] [ 0 ] . node_features. le_flags( ) , & [ 0b11 ] ) ;
23542363 assert_eq ! ( route. paths[ 0 ] [ 0 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
23552364
23562365 assert_eq ! ( route. paths[ 0 ] [ 1 ] . pubkey, target_node_id) ;
23572366 assert_eq ! ( route. paths[ 0 ] [ 1 ] . short_channel_id, 8 ) ;
2358- assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 100 ) ;
2367+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 1000000 ) ;
23592368 assert_eq ! ( route. paths[ 0 ] [ 1 ] . cltv_expiry_delta, 42 ) ;
23602369 assert_eq ! ( route. paths[ 0 ] [ 1 ] . node_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We dont pass flags in from invoices yet
23612370 assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
2371+
2372+ }
2373+
2374+ #[ test]
2375+ fn overflow_unannounced_path_test_liquidity_underflow ( ) {
2376+ // Previously, when we had a last-hop hint connected directly to a first-hop channel, where
2377+ // the last-hop had a value-overflowing fee, we'd panic.
2378+ // This was due to us adding the first-hop from us unconditionally, causing us to think
2379+ // we'd built a path (as our node is in the "best candidate" set), when we had not.
2380+ // In this test, we previously hit a subtraction underflow due to having less available
2381+ // liquidity at a hop than 0.
2382+ assert ! ( do_unannounced_path_test( Some ( 21_000_000_0000_0000_000 ) , 0 , 21_000_000_0000_0000_000 , 21_000_000_0000_0000_000 ) . is_err( ) ) ;
23622383 }
23632384
2385+ #[ test]
2386+ fn overflow_unannounced_path_test_feerate_overflow ( ) {
2387+ // This tests for the same case as above, except instead of hitting a subtraction
2388+ // underflow, we hit a case where the fee charged at a hop overflowed.
2389+ assert ! ( do_unannounced_path_test( Some ( 21_000_000_0000_0000_000 ) , 50000 , 21_000_000_0000_0000_000 , 21_000_000_0000_0000_000 ) . is_err( ) ) ;
2390+ }
2391+
2392+
23642393 #[ test]
23652394 fn available_amount_while_routing_test ( ) {
23662395 // Tests whether we choose the correct available channel amount while routing.
0 commit comments