@@ -513,8 +513,11 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
513
513
// $directional_info.
514
514
// $next_hops_fee_msat represents the fees paid for using all the channel *after* this one,
515
515
// since that value has to be transferred over this channel.
516
+ // Returns whether this channel caused an update to `targets`.
516
517
( $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 ;
518
521
// Channels to self should not be used. This is more of belt-and-suspenders, because in
519
522
// practice these cases should be caught earlier:
520
523
// - for regular channels at channel announcement (TODO)
@@ -726,6 +729,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
726
729
{
727
730
old_entry. value_contribution_msat = value_contribution_msat;
728
731
}
732
+ did_add_update_path_to_src_node = true ;
729
733
} else if old_entry. was_processed && new_cost < old_cost {
730
734
#[ cfg( any( test, feature = "fuzztarget" ) ) ]
731
735
{
@@ -756,7 +760,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
756
760
}
757
761
}
758
762
}
759
- } ;
763
+ did_add_update_path_to_src_node
764
+ } }
760
765
}
761
766
762
767
let empty_node_features = NodeFeatures :: empty ( ) ;
@@ -859,22 +864,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
859
864
// it matters only if the fees are exactly the same.
860
865
for hop in last_hops. iter ( ) {
861
866
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 ( ) ;
878
871
if have_hop_src_in_graph {
879
872
// BOLT 11 doesn't allow inclusion of features for the last hop hints, which
880
873
// 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
888
881
htlc_maximum_msat : hop. htlc_maximum_msat ,
889
882
fees : hop. fees ,
890
883
} ;
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
+ }
892
896
}
893
897
}
894
898
@@ -1159,7 +1163,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
1159
1163
1160
1164
#[ cfg( test) ]
1161
1165
mod tests {
1162
- use routing:: router:: { get_route, RouteHint , RouteHintHop , RoutingFees } ;
1166
+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
1163
1167
use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
1164
1168
use chain:: transaction:: OutPoint ;
1165
1169
use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -2307,11 +2311,7 @@ mod tests {
2307
2311
assert_eq ! ( route. paths[ 0 ] [ 4 ] . channel_features. le_flags( ) , & Vec :: <u8 >:: new( ) ) ; // We can't learn any flags from invoices, sadly
2308
2312
}
2309
2313
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 > {
2315
2315
let source_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 41 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2316
2316
let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2317
2317
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 {
2322
2322
short_channel_id: 8 ,
2323
2323
fees: RoutingFees {
2324
2324
base_msat: 1000 ,
2325
- proportional_millionths: 0 ,
2325
+ proportional_millionths: last_hop_fee_prop ,
2326
2326
} ,
2327
2327
cltv_expiry_delta: ( 8 << 8 ) | 1 ,
2328
2328
htlc_minimum_msat: None ,
2329
- htlc_maximum_msat: None ,
2329
+ htlc_maximum_msat: last_hop_htlc_max ,
2330
2330
} ] ) ;
2331
2331
let our_chans = vec ! [ channelmanager:: ChannelDetails {
2332
2332
channel_id: [ 0 ; 32 ] ,
@@ -2336,31 +2336,59 @@ mod tests {
2336
2336
counterparty_features: InitFeatures :: from_le_bytes( vec![ 0b11 ] ) ,
2337
2337
channel_value_satoshis: 100000 ,
2338
2338
user_id: 0 ,
2339
- outbound_capacity_msat: 100000 ,
2339
+ outbound_capacity_msat: outbound_capacity_msat ,
2340
2340
inbound_capacity_msat: 100000 ,
2341
2341
is_outbound: true , is_funding_locked: true ,
2342
2342
is_usable: true , is_public: true ,
2343
2343
counterparty_forwarding_info: None ,
2344
2344
} ] ;
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
+ }
2346
2347
2348
+ #[ test]
2349
+ fn unannounced_path_test ( ) {
2350
+ // We should be able to send a payment to a destination without any help of a routing graph
2351
+ // if we have a channel with a common counterparty that appears in the first and last hop
2352
+ // hints.
2353
+ let route = do_unannounced_path_test ( None , 1 , 2000000 , 1000000 ) . unwrap ( ) ;
2354
+
2355
+ let middle_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 42 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2356
+ let target_node_id = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02}" , 43 ) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( ) ) ;
2347
2357
assert_eq ! ( route. paths[ 0 ] . len( ) , 2 ) ;
2348
2358
2349
2359
assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, middle_node_id) ;
2350
2360
assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 42 ) ;
2351
- assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1000 ) ;
2361
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1001 ) ;
2352
2362
assert_eq ! ( route. paths[ 0 ] [ 0 ] . cltv_expiry_delta, ( 8 << 8 ) | 1 ) ;
2353
2363
assert_eq ! ( route. paths[ 0 ] [ 0 ] . node_features. le_flags( ) , & [ 0b11 ] ) ;
2354
2364
assert_eq ! ( route. paths[ 0 ] [ 0 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
2355
2365
2356
2366
assert_eq ! ( route. paths[ 0 ] [ 1 ] . pubkey, target_node_id) ;
2357
2367
assert_eq ! ( route. paths[ 0 ] [ 1 ] . short_channel_id, 8 ) ;
2358
- assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 100 ) ;
2368
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 1000000 ) ;
2359
2369
assert_eq ! ( route. paths[ 0 ] [ 1 ] . cltv_expiry_delta, 42 ) ;
2360
2370
assert_eq ! ( route. paths[ 0 ] [ 1 ] . node_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We dont pass flags in from invoices yet
2361
2371
assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & [ 0 ; 0 ] ) ; // We can't learn any flags from invoices, sadly
2362
2372
}
2363
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 fee which overflowed a u64, 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 the last 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( ) ) ;
2383
+ }
2384
+
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
+
2364
2392
#[ test]
2365
2393
fn available_amount_while_routing_test ( ) {
2366
2394
// Tests whether we choose the correct available channel amount while routing.
0 commit comments