@@ -7695,6 +7695,152 @@ mod tests {
7695
7695
assert_eq ! ( route. paths. len( ) , 1 ) ;
7696
7696
assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
7697
7697
}
7698
+
7699
+ #[ test]
7700
+ fn first_hop_preferred_over_hint ( ) {
7701
+ // Check that if we have a first hop to a peer we'd always prefer that over a route hint
7702
+ // they gave us, but we'd still consider all subsequent hints if they are more attractive.
7703
+ let secp_ctx = Secp256k1 :: new ( ) ;
7704
+ let logger = Arc :: new ( ln_test_utils:: TestLogger :: new ( ) ) ;
7705
+ let network_graph = Arc :: new ( NetworkGraph :: new ( Network :: Testnet , Arc :: clone ( & logger) ) ) ;
7706
+ let gossip_sync = P2PGossipSync :: new ( Arc :: clone ( & network_graph) , None , Arc :: clone ( & logger) ) ;
7707
+ let scorer = ln_test_utils:: TestScorer :: new ( ) ;
7708
+ let keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
7709
+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
7710
+ let config = UserConfig :: default ( ) ;
7711
+
7712
+ let amt_msat = 1_000_000 ;
7713
+ let ( our_privkey, our_node_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
7714
+
7715
+ add_channel ( & gossip_sync, & secp_ctx, & our_privkey, & privkeys[ 0 ] ,
7716
+ ChannelFeatures :: from_le_bytes ( id_to_feature_flags ( 1 ) ) , 1 ) ;
7717
+ update_channel ( & gossip_sync, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
7718
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
7719
+ short_channel_id : 1 ,
7720
+ timestamp : 1 ,
7721
+ flags : 0 ,
7722
+ cltv_expiry_delta : 42 ,
7723
+ htlc_minimum_msat : 1_000 ,
7724
+ htlc_maximum_msat : 10_000_000 ,
7725
+ fee_base_msat : 800 ,
7726
+ fee_proportional_millionths : 0 ,
7727
+ excess_data : Vec :: new ( )
7728
+ } ) ;
7729
+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 0 ] , UnsignedChannelUpdate {
7730
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
7731
+ short_channel_id : 1 ,
7732
+ timestamp : 1 ,
7733
+ flags : 1 ,
7734
+ cltv_expiry_delta : 42 ,
7735
+ htlc_minimum_msat : 1_000 ,
7736
+ htlc_maximum_msat : 10_000_000 ,
7737
+ fee_base_msat : 800 ,
7738
+ fee_proportional_millionths : 0 ,
7739
+ excess_data : Vec :: new ( )
7740
+ } ) ;
7741
+
7742
+ add_channel ( & gossip_sync, & secp_ctx, & privkeys[ 0 ] , & privkeys[ 1 ] ,
7743
+ ChannelFeatures :: from_le_bytes ( id_to_feature_flags ( 1 ) ) , 2 ) ;
7744
+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 0 ] , UnsignedChannelUpdate {
7745
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
7746
+ short_channel_id : 2 ,
7747
+ timestamp : 2 ,
7748
+ flags : 0 ,
7749
+ cltv_expiry_delta : 42 ,
7750
+ htlc_minimum_msat : 1_000 ,
7751
+ htlc_maximum_msat : 10_000_000 ,
7752
+ fee_base_msat : 800 ,
7753
+ fee_proportional_millionths : 0 ,
7754
+ excess_data : Vec :: new ( )
7755
+ } ) ;
7756
+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 1 ] , UnsignedChannelUpdate {
7757
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
7758
+ short_channel_id : 2 ,
7759
+ timestamp : 2 ,
7760
+ flags : 1 ,
7761
+ cltv_expiry_delta : 42 ,
7762
+ htlc_minimum_msat : 1_000 ,
7763
+ htlc_maximum_msat : 10_000_000 ,
7764
+ fee_base_msat : 800 ,
7765
+ fee_proportional_millionths : 0 ,
7766
+ excess_data : Vec :: new ( )
7767
+ } ) ;
7768
+
7769
+ let dest_node_id = nodes[ 2 ] ;
7770
+
7771
+ let route_hint = RouteHint ( vec ! [ RouteHintHop {
7772
+ src_node_id: our_node_id,
7773
+ short_channel_id: 44 ,
7774
+ fees: RoutingFees {
7775
+ base_msat: 234 ,
7776
+ proportional_millionths: 0 ,
7777
+ } ,
7778
+ cltv_expiry_delta: 10 ,
7779
+ htlc_minimum_msat: None ,
7780
+ htlc_maximum_msat: Some ( 5_000_000 ) ,
7781
+ } ,
7782
+ RouteHintHop {
7783
+ src_node_id: nodes[ 0 ] ,
7784
+ short_channel_id: 45 ,
7785
+ fees: RoutingFees {
7786
+ base_msat: 123 ,
7787
+ proportional_millionths: 0 ,
7788
+ } ,
7789
+ cltv_expiry_delta: 10 ,
7790
+ htlc_minimum_msat: None ,
7791
+ htlc_maximum_msat: None ,
7792
+ } ] ) ;
7793
+
7794
+ let payment_params = PaymentParameters :: from_node_id ( dest_node_id, 42 )
7795
+ . with_route_hints ( vec ! [ route_hint] ) . unwrap ( )
7796
+ . with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ;
7797
+ let route_params = RouteParameters :: from_payment_params_and_value (
7798
+ payment_params, amt_msat) ;
7799
+
7800
+ // First create an insufficient first hop for channel with SCID 1 and check we'd use the
7801
+ // route hint.
7802
+ let first_hop = get_channel_details ( Some ( 1 ) , nodes[ 0 ] ,
7803
+ channelmanager:: provided_init_features ( & config) , 999_999 ) ;
7804
+ let first_hops = vec ! [ first_hop] ;
7805
+
7806
+ let route = get_route ( & our_node_id, & route_params. clone ( ) , & network_graph. read_only ( ) ,
7807
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , Arc :: clone ( & logger) , & scorer,
7808
+ & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
7809
+ assert_eq ! ( route. paths. len( ) , 1 ) ;
7810
+ assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
7811
+ assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 2 ) ;
7812
+ assert_eq ! ( route. paths[ 0 ] . hops[ 0 ] . short_channel_id, 44 ) ;
7813
+ assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . short_channel_id, 45 ) ;
7814
+ assert_eq ! ( route. get_total_fees( ) , 123 ) ;
7815
+
7816
+ // Now check we would trust our first hop info, i.e., fail if we detect the route hint is
7817
+ // for a first hop channel.
7818
+ let mut first_hop = get_channel_details ( Some ( 1 ) , nodes[ 0 ] , channelmanager:: provided_init_features ( & config) , 999_999 ) ;
7819
+ first_hop. outbound_scid_alias = Some ( 44 ) ;
7820
+ let first_hops = vec ! [ first_hop] ;
7821
+
7822
+ let route_res = get_route ( & our_node_id, & route_params. clone ( ) , & network_graph. read_only ( ) ,
7823
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , Arc :: clone ( & logger) , & scorer,
7824
+ & Default :: default ( ) , & random_seed_bytes) ;
7825
+ assert ! ( route_res. is_err( ) ) ;
7826
+
7827
+ // Finally check we'd use the first hop if has sufficient outbound capacity. But we'd stil
7828
+ // use the cheaper second hop of the route hint.
7829
+ let mut first_hop = get_channel_details ( Some ( 1 ) , nodes[ 0 ] ,
7830
+ channelmanager:: provided_init_features ( & config) , 10_000_000 ) ;
7831
+ first_hop. outbound_scid_alias = Some ( 44 ) ;
7832
+ let first_hops = vec ! [ first_hop] ;
7833
+
7834
+ let route = get_route ( & our_node_id, & route_params. clone ( ) , & network_graph. read_only ( ) ,
7835
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , Arc :: clone ( & logger) , & scorer,
7836
+ & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
7837
+ assert_eq ! ( route. paths. len( ) , 1 ) ;
7838
+ assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
7839
+ assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 2 ) ;
7840
+ assert_eq ! ( route. paths[ 0 ] . hops[ 0 ] . short_channel_id, 1 ) ;
7841
+ assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . short_channel_id, 45 ) ;
7842
+ assert_eq ! ( route. get_total_fees( ) , 123 ) ;
7843
+ }
7698
7844
}
7699
7845
7700
7846
#[ cfg( all( any( test, ldk_bench) , not( feature = "no-std" ) ) ) ]
0 commit comments