@@ -143,7 +143,10 @@ struct RouteGraphNode {
143
143
// - how much is needed for a path being constructed
144
144
// - how much value can channels following this node (up to the destination) can contribute,
145
145
// considering their capacity and fees
146
- value_contribution_msat : u64
146
+ value_contribution_msat : u64 ,
147
+ /// The maximum htlc_minimum_msat along the path, taking into consideration the fees required
148
+ /// to meet the minimum over the hops required to get there.
149
+ path_htlc_minimum_msat : u64 ,
147
150
}
148
151
149
152
impl cmp:: Ord for RouteGraphNode {
@@ -487,7 +490,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
487
490
// $next_hops_fee_msat represents the fees paid for using all the channel *after* this one,
488
491
// since that value has to be transferred over this channel.
489
492
( $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,
490
- $next_hops_value_contribution: expr ) => {
493
+ $next_hops_value_contribution: expr, $incl_fee_next_hops_htlc_minimum_msat : expr ) => {
491
494
// Channels to self should not be used. This is more of belt-and-suspenders, because in
492
495
// practice these cases should be caught earlier:
493
496
// - for regular channels at channel announcement (TODO)
@@ -554,19 +557,27 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
554
557
// Can't overflow due to how the values were computed right above.
555
558
None => unreachable!( ) ,
556
559
} ;
560
+ #[ allow( unused_comparisons) ] // $incl_fee_next_hops_htlc_minimum_msat is 0 in some calls so rustc complains
561
+ let over_path_minimum_msat = amount_to_transfer_over_msat >= $directional_info. htlc_minimum_msat &&
562
+ amount_to_transfer_over_msat >= $incl_fee_next_hops_htlc_minimum_msat;
557
563
558
564
// If HTLC minimum is larger than the amount we're going to transfer, we shouldn't
559
565
// bother considering this channel.
560
566
// Since we're choosing amount_to_transfer_over_msat as maximum possible, it can
561
567
// be only reduced later (not increased), so this channel should just be skipped
562
568
// as not sufficient.
563
- if amount_to_transfer_over_msat < $directional_info . htlc_minimum_msat {
569
+ if !over_path_minimum_msat {
564
570
hit_minimum_limit = true ;
565
571
} else if contributes_sufficient_value {
566
572
// Note that low contribution here (limited by available_liquidity_msat)
567
573
// might violate htlc_minimum_msat on the hops which are next along the
568
574
// payment path (upstream to the payee). To avoid that, we recompute path
569
575
// path fees knowing the final path contribution after constructing it.
576
+ let path_htlc_minimum_msat = match compute_fees( $incl_fee_next_hops_htlc_minimum_msat, $directional_info. fees)
577
+ . map( |fee_msat| fee_msat. checked_add( $incl_fee_next_hops_htlc_minimum_msat) ) {
578
+ Some ( Some ( value_msat) ) => cmp:: max( value_msat, $directional_info. htlc_minimum_msat) ,
579
+ _ => u64 :: max_value( )
580
+ } ;
570
581
let hm_entry = dist. entry( & $src_node_id) ;
571
582
let old_entry = hm_entry. or_insert_with( || {
572
583
// If there was previously no known way to access
@@ -638,6 +649,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
638
649
lowest_fee_to_peer_through_node: total_fee_msat,
639
650
lowest_fee_to_node: $next_hops_fee_msat as u64 + hop_use_fee_msat,
640
651
value_contribution_msat: value_contribution_msat,
652
+ path_htlc_minimum_msat,
641
653
} ;
642
654
643
655
// Update the way of reaching $src_node_id with the given $chan_id (from $dest_node_id),
@@ -697,10 +709,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
697
709
// meaning how much will be paid in fees after this node (to the best of our knowledge).
698
710
// This data can later be helpful to optimize routing (pay lower fees).
699
711
macro_rules! add_entries_to_cheapest_to_target_node {
700
- ( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr ) => {
712
+ ( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr, $incl_fee_next_hops_htlc_minimum_msat : expr ) => {
701
713
if first_hops. is_some( ) {
702
714
if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat) ) = first_hop_targets. get( & $node_id) {
703
- 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) ;
715
+ 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, $incl_fee_next_hops_htlc_minimum_msat ) ;
704
716
}
705
717
}
706
718
@@ -720,15 +732,15 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
720
732
if first_hops. is_none( ) || chan. node_two != * our_node_id {
721
733
if let Some ( two_to_one) = chan. two_to_one. as_ref( ) {
722
734
if two_to_one. enabled {
723
- add_entry!( chan_id, chan. node_two, chan. node_one, two_to_one, chan. capacity_sats, chan. features, $fee_to_target_msat, $next_hops_value_contribution) ;
735
+ add_entry!( chan_id, chan. node_two, chan. node_one, two_to_one, chan. capacity_sats, chan. features, $fee_to_target_msat, $next_hops_value_contribution, $incl_fee_next_hops_htlc_minimum_msat ) ;
724
736
}
725
737
}
726
738
}
727
739
} else {
728
740
if first_hops. is_none( ) || chan. node_one != * our_node_id {
729
741
if let Some ( one_to_two) = chan. one_to_two. as_ref( ) {
730
742
if one_to_two. enabled {
731
- add_entry!( chan_id, chan. node_one, chan. node_two, one_to_two, chan. capacity_sats, chan. features, $fee_to_target_msat, $next_hops_value_contribution) ;
743
+ add_entry!( chan_id, chan. node_one, chan. node_two, one_to_two, chan. capacity_sats, chan. features, $fee_to_target_msat, $next_hops_value_contribution, $incl_fee_next_hops_htlc_minimum_msat ) ;
732
744
}
733
745
}
734
746
@@ -755,7 +767,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
755
767
// place where it could be added.
756
768
if first_hops. is_some ( ) {
757
769
if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat) ) = first_hop_targets. get ( & payee) {
758
- add_entry ! ( first_hop, * our_node_id, payee, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features. to_context( ) , 0 , path_value_msat) ;
770
+ add_entry ! ( first_hop, * our_node_id, payee, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features. to_context( ) , 0 , path_value_msat, 0 ) ;
759
771
}
760
772
}
761
773
@@ -768,7 +780,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
768
780
// If not, targets.pop() will not even let us enter the loop in step 2.
769
781
None => { } ,
770
782
Some ( node) => {
771
- add_entries_to_cheapest_to_target_node ! ( node, payee, 0 , path_value_msat) ;
783
+ add_entries_to_cheapest_to_target_node ! ( node, payee, 0 , path_value_msat, 0 ) ;
772
784
} ,
773
785
}
774
786
@@ -787,7 +799,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
787
799
// bit lazy here. In the future, we should pull them out via our
788
800
// ChannelManager, but there's no reason to waste the space until we
789
801
// need them.
790
- add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features. to_context( ) , 0 , path_value_msat) ;
802
+ add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features. to_context( ) , 0 , path_value_msat, 0 ) ;
791
803
true
792
804
} else {
793
805
// In any other case, only add the hop if the source is in the regular network
@@ -797,17 +809,17 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
797
809
if have_hop_src_in_graph {
798
810
// BOLT 11 doesn't allow inclusion of features for the last hop hints, which
799
811
// really sucks, cause we're gonna need that eventually.
800
- let last_hop_htlc_minimum_msat : u64 = match hop. htlc_minimum_msat {
812
+ let last_path_htlc_minimum_msat : u64 = match hop. htlc_minimum_msat {
801
813
Some ( htlc_minimum_msat) => htlc_minimum_msat,
802
814
None => 0
803
815
} ;
804
816
let directional_info = DummyDirectionalChannelInfo {
805
817
cltv_expiry_delta : hop. cltv_expiry_delta as u32 ,
806
- htlc_minimum_msat : last_hop_htlc_minimum_msat ,
818
+ htlc_minimum_msat : last_path_htlc_minimum_msat ,
807
819
htlc_maximum_msat : hop. htlc_maximum_msat ,
808
820
fees : hop. fees ,
809
821
} ;
810
- add_entry ! ( hop. short_channel_id, hop. src_node_id, payee, directional_info, None :: <u64 >, ChannelFeatures :: empty( ) , 0 , path_value_msat) ;
822
+ add_entry ! ( hop. short_channel_id, hop. src_node_id, payee, directional_info, None :: <u64 >, ChannelFeatures :: empty( ) , 0 , path_value_msat, 0 ) ;
811
823
}
812
824
}
813
825
@@ -824,7 +836,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
824
836
// Both these cases (and other cases except reaching recommended_value_msat) mean that
825
837
// paths_collection will be stopped because found_new_path==false.
826
838
// This is not necessarily a routing failure.
827
- ' path_construction: while let Some ( RouteGraphNode { pubkey, lowest_fee_to_node, value_contribution_msat, .. } ) = targets. pop ( ) {
839
+ ' path_construction: while let Some ( RouteGraphNode { pubkey, lowest_fee_to_node, value_contribution_msat, path_htlc_minimum_msat , .. } ) = targets. pop ( ) {
828
840
829
841
// Since we're going payee-to-payer, hitting our node as a target means we should stop
830
842
// traversing the graph and arrange the path out of what we found.
@@ -921,7 +933,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
921
933
match network. get_nodes ( ) . get ( & pubkey) {
922
934
None => { } ,
923
935
Some ( node) => {
924
- add_entries_to_cheapest_to_target_node ! ( node, & pubkey, lowest_fee_to_node, value_contribution_msat) ;
936
+ add_entries_to_cheapest_to_target_node ! ( node, & pubkey, lowest_fee_to_node, value_contribution_msat, path_htlc_minimum_msat ) ;
925
937
} ,
926
938
}
927
939
}
@@ -3523,6 +3535,66 @@ mod tests {
3523
3535
assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & id_to_feature_flags( 13 ) ) ;
3524
3536
}
3525
3537
}
3538
+
3539
+ #[ test]
3540
+ fn htlc_max_reduction_below_min ( ) {
3541
+ // Test that if, while walking the graph, we reduce the value being sent to meet an
3542
+ // htlc_maximum_msat, we don't end up undershooting a later htlc_minimum_msat. In the
3543
+ // initial version of MPP we'd accept such routes but reject them while recalculating fees,
3544
+ // resulting in us thinking there is no possible path, even if other paths exist.
3545
+ let ( secp_ctx, net_graph_msg_handler, _, logger) = build_graph ( ) ;
3546
+ let ( our_privkey, our_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
3547
+
3548
+ // We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
3549
+ // gets an htlc_minimum_msat of 80_000 and channel 4 an htlc_maximum_msat of 90_000. We
3550
+ // then try to send 90_000.
3551
+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
3552
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
3553
+ short_channel_id : 2 ,
3554
+ timestamp : 2 ,
3555
+ flags : 0 ,
3556
+ cltv_expiry_delta : 0 ,
3557
+ htlc_minimum_msat : 0 ,
3558
+ htlc_maximum_msat : OptionalField :: Present ( 80_000 ) ,
3559
+ fee_base_msat : 0 ,
3560
+ fee_proportional_millionths : 0 ,
3561
+ excess_data : Vec :: new ( )
3562
+ } ) ;
3563
+ update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 1 ] , UnsignedChannelUpdate {
3564
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
3565
+ short_channel_id : 4 ,
3566
+ timestamp : 2 ,
3567
+ flags : 0 ,
3568
+ cltv_expiry_delta : ( 4 << 8 ) | 1 ,
3569
+ htlc_minimum_msat : 90_000 ,
3570
+ htlc_maximum_msat : OptionalField :: Absent ,
3571
+ fee_base_msat : 0 ,
3572
+ fee_proportional_millionths : 0 ,
3573
+ excess_data : Vec :: new ( )
3574
+ } ) ;
3575
+
3576
+ {
3577
+ // Now, attempt to route 125 sats (just a bit below the capacity of 3 channels).
3578
+ // Our algorithm should provide us with these 3 paths.
3579
+ let route = get_route ( & our_id, & net_graph_msg_handler. network_graph . read ( ) . unwrap ( ) , & nodes[ 2 ] , Some ( InvoiceFeatures :: known ( ) ) , None , & Vec :: new ( ) , 90_000 , 42 , Arc :: clone ( & logger) ) . unwrap ( ) ;
3580
+ assert_eq ! ( route. paths. len( ) , 1 ) ;
3581
+ assert_eq ! ( route. paths[ 0 ] . len( ) , 2 ) ;
3582
+
3583
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 7 ] ) ;
3584
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 12 ) ;
3585
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 90_000 * 2 ) ;
3586
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . cltv_expiry_delta, ( 13 << 8 ) | 1 ) ;
3587
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . node_features. le_flags( ) , & id_to_feature_flags( 8 ) ) ;
3588
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . channel_features. le_flags( ) , & id_to_feature_flags( 12 ) ) ;
3589
+
3590
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . pubkey, nodes[ 2 ] ) ;
3591
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . short_channel_id, 13 ) ;
3592
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . fee_msat, 90_000 ) ;
3593
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . cltv_expiry_delta, 42 ) ;
3594
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . node_features. le_flags( ) , InvoiceFeatures :: known( ) . le_flags( ) ) ;
3595
+ assert_eq ! ( route. paths[ 0 ] [ 1 ] . channel_features. le_flags( ) , & id_to_feature_flags( 13 ) ) ;
3596
+ }
3597
+ }
3526
3598
}
3527
3599
3528
3600
#[ cfg( all( test, feature = "unstable" ) ) ]
0 commit comments