@@ -353,8 +353,10 @@ mod test {
353
353
use lightning:: util:: enforcing_trait_impls:: EnforcingSigner ;
354
354
use lightning:: util:: events:: { MessageSendEvent , MessageSendEventsProvider , Event } ;
355
355
use lightning:: util:: test_utils;
356
+ use lightning:: util:: config:: UserConfig ;
356
357
use lightning:: chain:: keysinterface:: KeysInterface ;
357
358
use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
359
+ use std:: collections:: HashSet ;
358
360
359
361
#[ test]
360
362
fn test_from_channelmanager ( ) {
@@ -417,6 +419,169 @@ mod test {
417
419
assert_eq ! ( events. len( ) , 2 ) ;
418
420
}
419
421
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
+
420
585
#[ test]
421
586
#[ cfg( feature = "std" ) ]
422
587
fn test_multi_node_receive ( ) {
0 commit comments