@@ -353,8 +353,10 @@ mod test {
353353 use lightning:: util:: enforcing_trait_impls:: EnforcingSigner ;
354354 use lightning:: util:: events:: { MessageSendEvent , MessageSendEventsProvider , Event } ;
355355 use lightning:: util:: test_utils;
356+ use lightning:: util:: config:: UserConfig ;
356357 use lightning:: chain:: keysinterface:: KeysInterface ;
357358 use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
359+ use std:: collections:: HashSet ;
358360
359361 #[ test]
360362 fn test_from_channelmanager ( ) {
@@ -417,6 +419,169 @@ mod test {
417419 assert_eq ! ( events. len( ) , 2 ) ;
418420 }
419421
422+ #[ test]
423+ fn test_hints_includes_single_channels_to_nodes ( ) {
424+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
425+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
426+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
427+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
428+
429+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
430+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
431+
432+ let mut scid_aliases = HashSet :: new ( ) ;
433+ scid_aliases. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
434+ scid_aliases. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
435+
436+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 0 ] , scid_aliases) ;
437+ }
438+
439+ #[ test]
440+ fn test_hints_has_only_highest_inbound_capacity_channel ( ) {
441+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
442+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
443+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
444+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
445+ let _chan_1_0_low_inbound_capacity = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
446+ let chan_1_0_high_inbound_capacity = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 10_000_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
447+ let _chan_1_0_medium_inbound_capacity = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 1_000_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
448+
449+ let mut scid_aliases = HashSet :: new ( ) ;
450+ scid_aliases. insert ( chan_1_0_high_inbound_capacity. 0 . short_channel_id_alias . unwrap ( ) ) ;
451+
452+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 0 ] , scid_aliases) ;
453+ }
454+
455+ #[ test]
456+ fn test_forwarding_info_not_assigned_channel_excluded_from_hints ( ) {
457+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
458+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
459+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
460+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
461+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
462+
463+ // Create an unannonced channel between `nodes[2]` and `nodes[0]`, for which the
464+ // `msgs::ChannelUpdate` is never handled for the node(s). As the `msgs::ChannelUpdate`
465+ // is never handled, the `channel.counterparty.forwarding_info` is never assigned.
466+ let mut private_chan_cfg = UserConfig :: default ( ) ;
467+ private_chan_cfg. channel_options . announced_channel = false ;
468+ let temporary_channel_id = nodes[ 2 ] . node . create_channel ( nodes[ 0 ] . node . get_our_node_id ( ) , 1_000_000 , 500_000_000 , 42 , Some ( private_chan_cfg) ) . unwrap ( ) ;
469+ let open_channel = get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendOpenChannel , nodes[ 0 ] . node. get_our_node_id( ) ) ;
470+ nodes[ 0 ] . node . handle_open_channel ( & nodes[ 2 ] . node . get_our_node_id ( ) , InitFeatures :: known ( ) , & open_channel) ;
471+ let accept_channel = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendAcceptChannel , nodes[ 2 ] . node. get_our_node_id( ) ) ;
472+ nodes[ 2 ] . node . handle_accept_channel ( & nodes[ 0 ] . node . get_our_node_id ( ) , InitFeatures :: known ( ) , & accept_channel) ;
473+
474+ let tx = sign_funding_transaction ( & nodes[ 2 ] , & nodes[ 0 ] , 1_000_000 , temporary_channel_id) ;
475+
476+ let conf_height = core:: cmp:: max ( nodes[ 2 ] . best_block_info ( ) . 1 + 1 , nodes[ 0 ] . best_block_info ( ) . 1 + 1 ) ;
477+ confirm_transaction_at ( & nodes[ 2 ] , & tx, conf_height) ;
478+ connect_blocks ( & nodes[ 2 ] , CHAN_CONFIRM_DEPTH - 1 ) ;
479+ confirm_transaction_at ( & nodes[ 0 ] , & tx, conf_height) ;
480+ connect_blocks ( & nodes[ 0 ] , CHAN_CONFIRM_DEPTH - 1 ) ;
481+ let as_funding_locked = get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendFundingLocked , nodes[ 0 ] . node. get_our_node_id( ) ) ;
482+ nodes[ 2 ] . node . handle_funding_locked ( & nodes[ 0 ] . node . get_our_node_id ( ) , & get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendFundingLocked , nodes[ 2 ] . node. get_our_node_id( ) ) ) ;
483+ get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendChannelUpdate , nodes[ 0 ] . node. get_our_node_id( ) ) ;
484+ nodes[ 0 ] . node . handle_funding_locked ( & nodes[ 2 ] . node . get_our_node_id ( ) , & as_funding_locked) ;
485+ get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendChannelUpdate , nodes[ 2 ] . node. get_our_node_id( ) ) ;
486+
487+ // As `msgs::ChannelUpdate` was never handled for the participating node(s) of the second
488+ // channel, the channel will never be assigned any `counterparty.forwarding_info`.
489+ // Therefore only `chan_1_0` should be included in the hints.
490+ let mut scid_aliases = HashSet :: new ( ) ;
491+ scid_aliases. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
492+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 0 ] , scid_aliases) ;
493+ }
494+
495+ #[ test]
496+ fn test_no_hints_if_a_mix_between_public_and_private_channel_exists ( ) {
497+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
498+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
499+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
500+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
501+ let _chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
502+
503+ let chan_2_0 = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
504+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_2_0. 1 ) ;
505+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_2_0. 0 ) ;
506+
507+ // Ensure that the invoice doesn't include any route hints for any of `nodes[0]` channels,
508+ // even though all channels between `nodes[1]` and `nodes[0]` are private, as there is a
509+ // public channel between `nodes[2]` and `nodes[0]`
510+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 0 ] , HashSet :: new ( ) ) ;
511+ }
512+
513+ #[ test]
514+ fn test_only_public_channels_includes_no_channels_in_hints ( ) {
515+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
516+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
517+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
518+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
519+ let chan_1_0 = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
520+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_1_0. 0 ) ;
521+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_1_0. 1 ) ;
522+
523+ let chan_2_0 = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
524+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_2_0. 1 ) ;
525+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_2_0. 0 ) ;
526+
527+ // As all of `nodes[0]` channels are public, no channels should be included in the hints
528+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 0 ] , HashSet :: new ( ) ) ;
529+ }
530+
531+ #[ test]
532+ fn test_channels_with_lower_inbound_capacity_than_invoice_amt_hints_filtering ( ) {
533+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
534+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
535+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
536+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
537+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
538+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 1_000_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
539+
540+ // As the invoice amt is 1 msat above chan_1_0's inbound capacity, it shouldn't be included
541+ let mut scid_aliases_99_000_001_msat = HashSet :: new ( ) ;
542+ scid_aliases_99_000_001_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
543+
544+ match_invoice_routes ( Some ( 99_000_001 ) , & nodes[ 0 ] , scid_aliases_99_000_001_msat) ;
545+
546+ // As the invoice amt is exactly at chan_1_0's inbound capacity, it should be included
547+ let mut scid_aliases_99_000_000_msat = HashSet :: new ( ) ;
548+ scid_aliases_99_000_000_msat. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
549+ scid_aliases_99_000_000_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
550+
551+ match_invoice_routes ( Some ( 99_000_000 ) , & nodes[ 0 ] , scid_aliases_99_000_000_msat) ;
552+
553+ // As the invoice amt is above all channels' inbound capacity, they will still be included
554+ let mut scid_aliases_2_000_000_000_msat = HashSet :: new ( ) ;
555+ scid_aliases_2_000_000_000_msat. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
556+ scid_aliases_2_000_000_000_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
557+
558+ match_invoice_routes ( Some ( 2_000_000_000 ) , & nodes[ 0 ] , scid_aliases_2_000_000_000_msat) ;
559+
560+ // An invoice with no specified amount should include all channels in the route hints.
561+ let mut scid_aliases_no_specified_amount = HashSet :: new ( ) ;
562+ scid_aliases_no_specified_amount. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
563+ scid_aliases_no_specified_amount. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
564+
565+ match_invoice_routes ( None , & nodes[ 0 ] , scid_aliases_no_specified_amount) ;
566+ }
567+
568+ fn match_invoice_routes < ' a , ' b : ' a , ' c : ' b > (
569+ invoice_amt : Option < u64 > ,
570+ invoice_node : & Node < ' a , ' b , ' c > ,
571+ mut chan_ids_to_match : HashSet < u64 >
572+ ) {
573+ let invoice = create_invoice_from_channelmanager_and_duration_since_epoch (
574+ & invoice_node. node , invoice_node. keys_manager , Currency :: BitcoinTestnet , invoice_amt, "test" . to_string ( ) ,
575+ Duration :: from_secs ( 1234567 ) ) . unwrap ( ) ;
576+ let hints = invoice. private_routes ( ) ;
577+
578+ for hint in hints {
579+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
580+ assert ! ( chan_ids_to_match. remove( & hint_short_chan_id) ) ;
581+ }
582+ assert ! ( chan_ids_to_match. is_empty( ) , "Unmatched short channel ids: {:?}" , chan_ids_to_match) ;
583+ }
584+
420585 #[ test]
421586 #[ cfg( feature = "std" ) ]
422587 fn test_multi_node_receive ( ) {
0 commit comments