@@ -514,8 +514,11 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
514514 // $directional_info.
515515 // $next_hops_fee_msat represents the fees paid for using all the channel *after* this one,
516516 // since that value has to be transferred over this channel.
517+ // Returns whether this channel caused an update to `targets`.
517518 ( $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,
518- $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr ) => {
519+ $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr ) => { {
520+ // We "return" whether we updated the path at the end, via this:
521+ let mut did_add_update_path_to_src_node = false ;
519522 // Channels to self should not be used. This is more of belt-and-suspenders, because in
520523 // practice these cases should be caught earlier:
521524 // - for regular channels at channel announcement (TODO)
@@ -727,6 +730,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
727730 {
728731 old_entry. value_contribution_msat = value_contribution_msat;
729732 }
733+ did_add_update_path_to_src_node = true ;
730734 } else if old_entry. was_processed && new_cost < old_cost {
731735 #[ cfg( any( test, feature = "fuzztarget" ) ) ]
732736 {
@@ -757,7 +761,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
757761 }
758762 }
759763 }
760- } ;
764+ did_add_update_path_to_src_node
765+ } }
761766 }
762767
763768 let empty_node_features = NodeFeatures :: empty ( ) ;
@@ -860,22 +865,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
860865 // it matters only if the fees are exactly the same.
861866 for hop in last_hops. iter ( ) {
862867 let have_hop_src_in_graph =
863- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & hop. src_node_id ) {
864- // If this hop connects to a node with which we have a direct channel, ignore
865- // the network graph and add both the hop and our direct channel to
866- // the candidate set.
867- //
868- // Currently there are no channel-context features defined, so we are a
869- // bit lazy here. In the future, we should pull them out via our
870- // ChannelManager, but there's no reason to waste the space until we
871- // need them.
872- 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 ) ;
873- true
874- } else {
875- // In any other case, only add the hop if the source is in the regular network
876- // graph:
877- network. get_nodes ( ) . get ( & hop. src_node_id ) . is_some ( )
878- } ;
868+ // Only add the last hop to our candidate set if either we have a direct channel or
869+ // they are in the regular network graph.
870+ first_hop_targets. get ( & hop. src_node_id ) . is_some ( ) ||
871+ network. get_nodes ( ) . get ( & hop. src_node_id ) . is_some ( ) ;
879872 if have_hop_src_in_graph {
880873 // BOLT 11 doesn't allow inclusion of features for the last hop hints, which
881874 // really sucks, cause we're gonna need that eventually.
@@ -889,7 +882,18 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
889882 htlc_maximum_msat : hop. htlc_maximum_msat ,
890883 fees : hop. fees ,
891884 } ;
892- 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 add_entry ! ( hop. short_channel_id, hop. src_node_id, payee, directional_info, None :: <u64 >, & empty_channel_features, 0 , path_value_msat, 0 ) {
886+ // If this hop connects to a node with which we have a direct channel,
887+ // ignore the network graph and, if the last hop was added, add our
888+ // direct channel to the candidate set.
889+ //
890+ // Note that we *must* check if the last hop was added as `add_entry`
891+ // always assumes that the third argument is a node to which we have a
892+ // path.
893+ if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & hop. src_node_id ) {
894+ 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 ) ;
895+ }
896+ }
893897 }
894898 }
895899
@@ -1170,7 +1174,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11701174
11711175#[ cfg( test) ]
11721176mod tests {
1173- use routing:: router:: { get_route, RouteHint , RouteHintHop , RoutingFees } ;
1177+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
11741178 use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
11751179 use chain:: transaction:: OutPoint ;
11761180 use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -2318,11 +2322,7 @@ mod tests {
23182322 assert_eq ! ( route. paths[ 0 ] [ 4 ] . channel_features. le_flags( ) , & Vec :: <u8 >:: new( ) ) ; // We can't learn any flags from invoices, sadly
23192323 }
23202324
2321- #[ test]
2322- fn unannounced_path_test ( ) {
2323- // We should be able to send a payment to a destination without any help of a routing graph
2324- // if we have a channel with a common counterparty that appears in the first and last hop
2325- // hints.
2325+ 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 > {
23262326 let source_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 41 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23272327 let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23282328 let target_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 43 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
@@ -2333,11 +2333,11 @@ mod tests {
23332333 short_channel_id: 8 ,
23342334 fees: RoutingFees {
23352335 base_msat: 1000 ,
2336- proportional_millionths: 0 ,
2336+ proportional_millionths: last_hop_fee_prop ,
23372337 } ,
23382338 cltv_expiry_delta: ( 8 << 8 ) | 1 ,
23392339 htlc_minimum_msat: None ,
2340- htlc_maximum_msat: None ,
2340+ htlc_maximum_msat: last_hop_htlc_max ,
23412341 } ] ) ;
23422342 let our_chans = vec ! [ channelmanager:: ChannelDetails {
23432343 channel_id: [ 0 ; 32 ] ,
@@ -2347,31 +2347,59 @@ mod tests {
23472347 counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
23482348 channel_value_satoshis: 100000 ,
23492349 user_id: 0 ,
2350- outbound_capacity_msat: 100000 ,
2350+ outbound_capacity_msat: outbound_capacity_msat ,
23512351 inbound_capacity_msat: 100000 ,
23522352 is_outbound: true , is_funding_locked: true ,
23532353 is_usable: true , is_public: true ,
23542354 counterparty_forwarding_info: None ,
23552355 } ] ;
2356- 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 ( ) ;
2356+ 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 ( ) ) )
2357+ }
23572358
2359+ #[ test]
2360+ fn unannounced_path_test ( ) {
2361+ // We should be able to send a payment to a destination without any help of a routing graph
2362+ // if we have a channel with a common counterparty that appears in the first and last hop
2363+ // hints.
2364+ let route = do_unannounced_path_test ( None , 1 , 2000000 , 1000000 ) . unwrap ( ) ;
2365+
2366+ let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2367+ let target_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 43 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
23582368 assert_eq ! ( route. paths[ 0 ] . len( ) , 2 ) ;
23592369
23602370 assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, middle_node_id) ;
23612371 assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 42 ) ;
2362- assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1000 ) ;
2372+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1001 ) ;
23632373 assert_eq ! ( route. paths[ 0 ] [ 0 ] . cltv_expiry_delta, ( 8 << 8 ) | 1 ) ;
23642374 assert_eq ! ( route. paths[ 0 ] [ 0 ] . node_features. le_flags( ) , & [ 0b11 ] ) ;
23652375 assert_eq ! ( route. paths[ 0 ] [ 0 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
23662376
23672377 assert_eq ! ( route. paths[ 0 ] [ 1 ] . pubkey, target_node_id) ;
23682378 assert_eq ! ( route. paths[ 0 ] [ 1 ] . short_channel_id, 8 ) ;
2369- assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 100 ) ;
2379+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 1000000 ) ;
23702380 assert_eq ! ( route. paths[ 0 ] [ 1 ] . cltv_expiry_delta, 42 ) ;
23712381 assert_eq ! ( route. paths[ 0 ] [ 1 ] . node_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We dont pass flags in from invoices yet
23722382 assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
23732383 }
23742384
2385+ #[ test]
2386+ fn overflow_unannounced_path_test_liquidity_underflow ( ) {
2387+ // Previously, when we had a last-hop hint connected directly to a first-hop channel, where
2388+ // the last-hop had a fee which overflowed a u64, we'd panic.
2389+ // This was due to us adding the first-hop from us unconditionally, causing us to think
2390+ // we'd built a path (as our node is in the "best candidate" set), when we had not.
2391+ // In this test, we previously hit a subtraction underflow due to having less available
2392+ // liquidity at the last hop than 0.
2393+ 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( ) ) ;
2394+ }
2395+
2396+ #[ test]
2397+ fn overflow_unannounced_path_test_feerate_overflow ( ) {
2398+ // This tests for the same case as above, except instead of hitting a subtraction
2399+ // underflow, we hit a case where the fee charged at a hop overflowed.
2400+ 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( ) ) ;
2401+ }
2402+
23752403 #[ test]
23762404 fn available_amount_while_routing_test ( ) {
23772405 // Tests whether we choose the correct available channel amount while routing.
0 commit comments