@@ -373,8 +373,10 @@ mod test {
373
373
use lightning:: util:: enforcing_trait_impls:: EnforcingSigner ;
374
374
use lightning:: util:: events:: { MessageSendEvent , MessageSendEventsProvider , Event } ;
375
375
use lightning:: util:: test_utils;
376
+ use lightning:: util:: config:: UserConfig ;
376
377
use lightning:: chain:: keysinterface:: KeysInterface ;
377
378
use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
379
+ use std:: collections:: HashSet ;
378
380
379
381
#[ test]
380
382
fn test_from_channelmanager ( ) {
@@ -437,6 +439,207 @@ mod test {
437
439
assert_eq ! ( events. len( ) , 2 ) ;
438
440
}
439
441
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
+
440
643
#[ test]
441
644
#[ cfg( feature = "std" ) ]
442
645
fn test_multi_node_receive ( ) {
0 commit comments