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