@@ -376,6 +376,7 @@ mod test {
376376 use lightning:: util:: config:: UserConfig ;
377377 use lightning:: chain:: keysinterface:: KeysInterface ;
378378 use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
379+ use std:: collections:: HashSet ;
379380
380381 #[ test]
381382 fn test_from_channelmanager ( ) {
@@ -440,6 +441,214 @@ mod test {
440441 assert_eq ! ( events. len( ) , 2 ) ;
441442 }
442443
444+ #[ test]
445+ fn test_hints_includes_single_channels_to_nodes ( ) {
446+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
447+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
448+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
449+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
450+
451+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
452+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
453+
454+ let mut scid_aliases = HashSet :: new ( ) ;
455+ scid_aliases. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
456+ scid_aliases. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
457+
458+ match_invoice_routes (
459+ Some ( 5000 ) ,
460+ & nodes[ 0 ] ,
461+ scid_aliases,
462+ ) ;
463+ }
464+
465+ #[ test]
466+ fn test_hints_has_only_highest_inbound_capacity_channel ( ) {
467+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
468+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
469+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
470+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
471+ let _chan_1_0_low_inbound_capacity = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
472+ 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 ( ) ) ;
473+ 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 ( ) ) ;
474+
475+ let mut scid_aliases = HashSet :: new ( ) ;
476+ scid_aliases. insert ( chan_1_0_high_inbound_capacity. 0 . short_channel_id_alias . unwrap ( ) ) ;
477+
478+ match_invoice_routes (
479+ Some ( 5000 ) ,
480+ & nodes[ 0 ] ,
481+ scid_aliases,
482+ ) ;
483+ }
484+
485+ #[ test]
486+ fn test_forwarding_info_not_assigned_channel_excluded_from_hints ( ) {
487+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
488+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
489+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
490+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
491+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
492+
493+ // Create an unannonced channel between `nodes[2]` and `nodes[0]`, for which the
494+ // `msgs::ChannelUpdate` is never handled for the node(s). As the `msgs::ChannelUpdate`
495+ // is never handled, the `channel.counterparty.forwarding_info` is never assigned.
496+ let mut private_chan_cfg = UserConfig :: default ( ) ;
497+ private_chan_cfg. channel_options . announced_channel = false ;
498+ nodes[ 2 ] . node . create_channel ( nodes[ 0 ] . node . get_our_node_id ( ) , 1_000_000 , 500_000_000 , 42 , Some ( private_chan_cfg) ) . unwrap ( ) ;
499+ let open_channel = get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendOpenChannel , nodes[ 0 ] . node. get_our_node_id( ) ) ;
500+ nodes[ 0 ] . node . handle_open_channel ( & nodes[ 2 ] . node . get_our_node_id ( ) , InitFeatures :: known ( ) , & open_channel) ;
501+ let accept_channel = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendAcceptChannel , nodes[ 2 ] . node. get_our_node_id( ) ) ;
502+ nodes[ 2 ] . node . handle_accept_channel ( & nodes[ 0 ] . node . get_our_node_id ( ) , InitFeatures :: known ( ) , & accept_channel) ;
503+
504+ let ( temporary_channel_id, tx, _) = create_funding_transaction ( & nodes[ 2 ] , 1_000_000 , 42 ) ;
505+ nodes[ 2 ] . node . funding_transaction_generated ( & temporary_channel_id, tx. clone ( ) ) . unwrap ( ) ;
506+ nodes[ 0 ] . node . handle_funding_created ( & nodes[ 2 ] . node . get_our_node_id ( ) , & get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendFundingCreated , nodes[ 0 ] . node. get_our_node_id( ) ) ) ;
507+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
508+
509+ let cs_funding_signed = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendFundingSigned , nodes[ 2 ] . node. get_our_node_id( ) ) ;
510+ nodes[ 2 ] . node . handle_funding_signed ( & nodes[ 0 ] . node . get_our_node_id ( ) , & cs_funding_signed) ;
511+ check_added_monitors ! ( nodes[ 2 ] , 1 ) ;
512+
513+ let conf_height = core:: cmp:: max ( nodes[ 2 ] . best_block_info ( ) . 1 + 1 , nodes[ 0 ] . best_block_info ( ) . 1 + 1 ) ;
514+ confirm_transaction_at ( & nodes[ 2 ] , & tx, conf_height) ;
515+ connect_blocks ( & nodes[ 2 ] , CHAN_CONFIRM_DEPTH - 1 ) ;
516+ confirm_transaction_at ( & nodes[ 0 ] , & tx, conf_height) ;
517+ connect_blocks ( & nodes[ 0 ] , CHAN_CONFIRM_DEPTH - 1 ) ;
518+ let as_funding_locked = get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendFundingLocked , nodes[ 0 ] . node. get_our_node_id( ) ) ;
519+ 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( ) ) ) ;
520+ get_event_msg ! ( nodes[ 2 ] , MessageSendEvent :: SendChannelUpdate , nodes[ 0 ] . node. get_our_node_id( ) ) ;
521+ nodes[ 0 ] . node . handle_funding_locked ( & nodes[ 2 ] . node . get_our_node_id ( ) , & as_funding_locked) ;
522+ get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendChannelUpdate , nodes[ 2 ] . node. get_our_node_id( ) ) ;
523+
524+ // As `msgs::ChannelUpdate` was never handled for the participating node(s) of the second
525+ // channel, the channel will never be assigned any `counterparty.forwarding_info`.
526+ // Therefore only `chan_1_0` should be included in the hints.
527+ let mut scid_aliases = HashSet :: new ( ) ;
528+ scid_aliases. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
529+ match_invoice_routes (
530+ Some ( 5000 ) ,
531+ & nodes[ 0 ] ,
532+ scid_aliases,
533+ ) ;
534+ }
535+
536+ #[ test]
537+ fn test_no_hints_if_a_mix_between_public_and_private_channel_exists ( ) {
538+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
539+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
540+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
541+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
542+ let _chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
543+
544+ let chan_2_0 = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
545+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_2_0. 1 ) ;
546+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_2_0. 0 ) ;
547+
548+ // Ensure that the invoice doesn't include any route hints for any of `nodes[0]` channels,
549+ // even though all channels between `nodes[1]` and `nodes[0]` are private, as there is a
550+ // public channel between `nodes[2]` and `nodes[0]`
551+ match_invoice_routes (
552+ Some ( 5000 ) ,
553+ & nodes[ 0 ] ,
554+ HashSet :: new ( ) ,
555+ ) ;
556+ }
557+
558+ #[ test]
559+ fn test_only_public_channels_includes_no_channels_in_hints ( ) {
560+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
561+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
562+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
563+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
564+ let chan_1_0 = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
565+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_1_0. 0 ) ;
566+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_1_0. 1 ) ;
567+
568+ let chan_2_0 = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
569+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_2_0. 1 ) ;
570+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_2_0. 0 ) ;
571+
572+ // As all of `nodes[0]` channels are public, no channels should be included in the hints
573+ match_invoice_routes (
574+ Some ( 5000 ) ,
575+ & nodes[ 0 ] ,
576+ HashSet :: new ( ) ,
577+ ) ;
578+ }
579+
580+ #[ test]
581+ fn test_channels_with_lower_inbound_capacity_than_invoice_amt_hints_filtering ( ) {
582+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
583+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
584+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
585+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
586+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 0 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
587+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 0 , 1_000_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
588+
589+ // As the invoice amt is 1 msat above chan_1_0's inbound capacity, it shouldn't be included
590+ let mut scid_aliases_99_000_001_msat = HashSet :: new ( ) ;
591+ scid_aliases_99_000_001_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
592+
593+ match_invoice_routes (
594+ Some ( 99_000_001 ) ,
595+ & nodes[ 0 ] ,
596+ scid_aliases_99_000_001_msat,
597+ ) ;
598+
599+ // As the invoice amt is exactly at chan_1_0's inbound capacity, it should be included
600+ let mut scid_aliases_99_000_000_msat = HashSet :: new ( ) ;
601+ scid_aliases_99_000_000_msat. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
602+ scid_aliases_99_000_000_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
603+
604+ match_invoice_routes (
605+ Some ( 99_000_000 ) ,
606+ & nodes[ 0 ] ,
607+ scid_aliases_99_000_000_msat,
608+ ) ;
609+
610+ // As the invoice amt is above all channels' inbound capacity, they will still be included
611+ let mut scid_aliases_2_000_000_000_msat = HashSet :: new ( ) ;
612+ scid_aliases_2_000_000_000_msat. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
613+ scid_aliases_2_000_000_000_msat. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
614+
615+ match_invoice_routes (
616+ Some ( 2_000_000_000 ) ,
617+ & nodes[ 0 ] ,
618+ scid_aliases_2_000_000_000_msat,
619+ ) ;
620+
621+ // An invoice with no specified amount should include all channels in the route hints.
622+ let mut scid_aliases_no_specified_amount = HashSet :: new ( ) ;
623+ scid_aliases_no_specified_amount. insert ( chan_1_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
624+ scid_aliases_no_specified_amount. insert ( chan_2_0. 0 . short_channel_id_alias . unwrap ( ) ) ;
625+
626+ match_invoice_routes (
627+ None ,
628+ & nodes[ 0 ] ,
629+ scid_aliases_no_specified_amount,
630+ ) ;
631+ }
632+
633+ fn match_invoice_routes < ' a , ' b : ' a , ' c : ' b > (
634+ invoice_amt : Option < u64 > ,
635+ invoice_node : & Node < ' a , ' b , ' c > ,
636+ mut chan_ids_to_match : HashSet < u64 >
637+ ) {
638+ let invoice = create_invoice_from_channelmanager_and_duration_since_epoch (
639+ & invoice_node. node , invoice_node. keys_manager , Currency :: BitcoinTestnet , invoice_amt, "test" . to_string ( ) ,
640+ Duration :: from_secs ( 1234567 ) ) . unwrap ( ) ;
641+ let hints = invoice. private_routes ( ) ;
642+
643+ assert_eq ! ( hints. len( ) , chan_ids_to_match. len( ) ) ;
644+
645+ for hint in hints {
646+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
647+ assert ! ( chan_ids_to_match. contains( & hint_short_chan_id) ) ;
648+ chan_ids_to_match. remove ( & hint_short_chan_id) ;
649+ }
650+ }
651+
443652 #[ test]
444653 #[ cfg( feature = "std" ) ]
445654 fn test_multi_node_receive ( ) {
0 commit comments