@@ -514,8 +514,11 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
514
514
// $directional_info.
515
515
// $next_hops_fee_msat represents the fees paid for using all the channel *after* this one,
516
516
// since that value has to be transferred over this channel.
517
+ // Returns whether this channel caused an update to `targets`.
517
518
( $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 ;
519
522
// Channels to self should not be used. This is more of belt-and-suspenders, because in
520
523
// practice these cases should be caught earlier:
521
524
// - for regular channels at channel announcement (TODO)
@@ -727,6 +730,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
727
730
{
728
731
old_entry. value_contribution_msat = value_contribution_msat;
729
732
}
733
+ did_add_update_path_to_src_node = true ;
730
734
} else if old_entry. was_processed && new_cost < old_cost {
731
735
#[ cfg( any( test, feature = "fuzztarget" ) ) ]
732
736
{
@@ -757,7 +761,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
757
761
}
758
762
}
759
763
}
760
- } ;
764
+ did_add_update_path_to_src_node
765
+ } }
761
766
}
762
767
763
768
let empty_node_features = NodeFeatures :: empty ( ) ;
@@ -860,22 +865,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
860
865
// it matters only if the fees are exactly the same.
861
866
for hop in last_hops. iter ( ) {
862
867
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 ( ) ;
879
872
if have_hop_src_in_graph {
880
873
// BOLT 11 doesn't allow inclusion of features for the last hop hints, which
881
874
// 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
889
882
htlc_maximum_msat : hop. htlc_maximum_msat ,
890
883
fees : hop. fees ,
891
884
} ;
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
+ }
893
897
}
894
898
}
895
899
@@ -1170,7 +1174,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
1170
1174
1171
1175
#[ cfg( test) ]
1172
1176
mod tests {
1173
- use routing:: router:: { get_route, RouteHint , RouteHintHop , RoutingFees } ;
1177
+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
1174
1178
use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
1175
1179
use chain:: transaction:: OutPoint ;
1176
1180
use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -2318,11 +2322,7 @@ mod tests {
2318
2322
assert_eq ! ( route. paths[ 0 ] [ 4 ] . channel_features. le_flags( ) , & Vec :: <u8 >:: new( ) ) ; // We can't learn any flags from invoices, sadly
2319
2323
}
2320
2324
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 > {
2326
2326
let source_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 41 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2327
2327
let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2328
2328
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 {
2333
2333
short_channel_id: 8 ,
2334
2334
fees: RoutingFees {
2335
2335
base_msat: 1000 ,
2336
- proportional_millionths: 0 ,
2336
+ proportional_millionths: last_hop_fee_prop ,
2337
2337
} ,
2338
2338
cltv_expiry_delta: ( 8 << 8 ) | 1 ,
2339
2339
htlc_minimum_msat: None ,
2340
- htlc_maximum_msat: None ,
2340
+ htlc_maximum_msat: last_hop_htlc_max ,
2341
2341
} ] ) ;
2342
2342
let our_chans = vec ! [ channelmanager:: ChannelDetails {
2343
2343
channel_id: [ 0 ; 32 ] ,
@@ -2347,31 +2347,59 @@ mod tests {
2347
2347
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
2348
2348
channel_value_satoshis: 100000 ,
2349
2349
user_id: 0 ,
2350
- outbound_capacity_msat: 100000 ,
2350
+ outbound_capacity_msat: outbound_capacity_msat ,
2351
2351
inbound_capacity_msat: 100000 ,
2352
2352
is_outbound: true , is_funding_locked: true ,
2353
2353
is_usable: true , is_public: true ,
2354
2354
counterparty_forwarding_info: None ,
2355
2355
} ] ;
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
+ }
2357
2358
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 ( ) ) ;
2358
2368
assert_eq ! ( route. paths[ 0 ] . len( ) , 2 ) ;
2359
2369
2360
2370
assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, middle_node_id) ;
2361
2371
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 ) ;
2363
2373
assert_eq ! ( route. paths[ 0 ] [ 0 ] . cltv_expiry_delta, ( 8 << 8 ) | 1 ) ;
2364
2374
assert_eq ! ( route. paths[ 0 ] [ 0 ] . node_features. le_flags( ) , & [ 0b11 ] ) ;
2365
2375
assert_eq ! ( route. paths[ 0 ] [ 0 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
2366
2376
2367
2377
assert_eq ! ( route. paths[ 0 ] [ 1 ] . pubkey, target_node_id) ;
2368
2378
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 ) ;
2370
2380
assert_eq ! ( route. paths[ 0 ] [ 1 ] . cltv_expiry_delta, 42 ) ;
2371
2381
assert_eq ! ( route. paths[ 0 ] [ 1 ] . node_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We dont pass flags in from invoices yet
2372
2382
assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
2373
2383
}
2374
2384
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
+
2375
2403
#[ test]
2376
2404
fn available_amount_while_routing_test ( ) {
2377
2405
// Tests whether we choose the correct available channel amount while routing.
0 commit comments