@@ -7695,6 +7695,152 @@ mod tests {
76957695 assert_eq ! ( route. paths. len( ) , 1 ) ;
76967696 assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
76977697 }
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+ }
76987844}
76997845
77007846#[ cfg( all( any( test, ldk_bench) , not( feature = "no-std" ) ) ) ]
0 commit comments