@@ -512,8 +512,10 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
512
512
/// * Always select the channel with the highest inbound capacity per counterparty node
513
513
/// * Prefer channels with capacity at least `min_inbound_capacity_msat` and where the channel
514
514
/// `is_usable` (i.e. the peer is connected).
515
- /// * If any public channel exists, the returned `RouteHint`s will be empty, and the sender will
516
- /// need to find the path by looking at the public channels instead
515
+ /// * If any public channel exists, only public [`RouteHint`]s will be returned.
516
+ /// * If any public, announced, channel exists (i.e. a channel with 7+ confs, to ensure the
517
+ /// announcement has had a chance to propagate), no [`RouteHint`]s will be returned, as the
518
+ /// sender is expected to find the path by looking at the public channels instead.
517
519
fn filter_channels < L : Deref > (
518
520
channels : Vec < ChannelDetails > , min_inbound_capacity_msat : Option < u64 > , logger : & L
519
521
) -> Vec < RouteHint > where L :: Target : Logger {
@@ -522,6 +524,7 @@ fn filter_channels<L: Deref>(
522
524
let mut min_capacity_channel_exists = false ;
523
525
let mut online_channel_exists = false ;
524
526
let mut online_min_capacity_channel_exists = false ;
527
+ let mut has_pub_unconf_chan = false ;
525
528
526
529
log_trace ! ( logger, "Considering {} channels for invoice route hints" , channels. len( ) ) ;
527
530
for channel in channels. into_iter ( ) . filter ( |chan| chan. is_channel_ready ) {
@@ -531,11 +534,18 @@ fn filter_channels<L: Deref>(
531
534
}
532
535
533
536
if channel. is_public {
534
- // If any public channel exists, return no hints and let the sender
535
- // look at the public channels instead.
536
- log_trace ! ( logger, "Not including channels in invoice route hints on account of public channel {}" ,
537
- log_bytes!( channel. channel_id) ) ;
538
- return vec ! [ ]
537
+ if channel. confirmations . is_some ( ) && channel. confirmations < Some ( 7 ) {
538
+ // If we have a public channel, but it doesn't have enough confirmations to (yet)
539
+ // be in the public network graph (and have gotten a chance to propagate), include
540
+ // route hints but only for public channels to protect private channel privacy.
541
+ has_pub_unconf_chan = true ;
542
+ } else {
543
+ // If any public channel exists, return no hints and let the sender
544
+ // look at the public channels instead.
545
+ log_trace ! ( logger, "Not including channels in invoice route hints on account of public channel {}" ,
546
+ log_bytes!( channel. channel_id) ) ;
547
+ return vec ! [ ]
548
+ }
539
549
}
540
550
541
551
if channel. inbound_capacity_msat >= min_inbound_capacity {
@@ -559,19 +569,26 @@ fn filter_channels<L: Deref>(
559
569
match filtered_channels. entry ( channel. counterparty . node_id ) {
560
570
hash_map:: Entry :: Occupied ( mut entry) => {
561
571
let current_max_capacity = entry. get ( ) . inbound_capacity_msat ;
562
- if channel. inbound_capacity_msat < current_max_capacity {
572
+ let new_now_public = channel. is_public && !entry. get ( ) . is_public ;
573
+ let new_higher_capacity = channel. is_public == entry. get ( ) . is_public &&
574
+ channel. inbound_capacity_msat > current_max_capacity;
575
+ if !new_now_public && !new_higher_capacity {
563
576
log_trace ! ( logger,
564
- "Preferring counterparty {} channel {} ({} msats) over {} ({} msats) for invoice route hints" ,
577
+ "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints" ,
565
578
log_pubkey!( channel. counterparty. node_id) ,
566
- log_bytes!( entry. get( ) . channel_id) , current_max_capacity,
567
- log_bytes!( channel. channel_id) , channel. inbound_capacity_msat) ;
579
+ log_bytes!( entry. get( ) . channel_id) , entry. get( ) . short_channel_id,
580
+ current_max_capacity,
581
+ log_bytes!( channel. channel_id) , channel. short_channel_id,
582
+ channel. inbound_capacity_msat) ;
568
583
continue ;
569
584
}
570
585
log_trace ! ( logger,
571
- "Preferring counterparty {} channel {} ({} msats) over {} ({} msats) for invoice route hints" ,
586
+ "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints" ,
572
587
log_pubkey!( channel. counterparty. node_id) ,
573
- log_bytes!( channel. channel_id) , channel. inbound_capacity_msat,
574
- log_bytes!( entry. get( ) . channel_id) , current_max_capacity) ;
588
+ log_bytes!( channel. channel_id) , channel. short_channel_id,
589
+ channel. inbound_capacity_msat,
590
+ log_bytes!( entry. get( ) . channel_id) , entry. get( ) . short_channel_id,
591
+ current_max_capacity) ;
575
592
entry. insert ( channel) ;
576
593
}
577
594
hash_map:: Entry :: Vacant ( entry) => {
@@ -602,7 +619,12 @@ fn filter_channels<L: Deref>(
602
619
. map ( |( _, channel) | channel)
603
620
. filter ( |channel| {
604
621
let has_enough_capacity = channel. inbound_capacity_msat >= min_inbound_capacity;
605
- let include_channel = if online_min_capacity_channel_exists {
622
+ let include_channel = if has_pub_unconf_chan {
623
+ // If we have a public channel, but it doesn't have enough confirmations to (yet)
624
+ // be in the public network graph (and have gotten a chance to propagate), include
625
+ // route hints but only for public channels to protect private channel privacy.
626
+ channel. is_public
627
+ } else if online_min_capacity_channel_exists {
606
628
has_enough_capacity && channel. is_usable
607
629
} else if min_capacity_channel_exists && online_channel_exists {
608
630
// If there are some online channels and some min_capacity channels, but no
@@ -622,7 +644,7 @@ fn filter_channels<L: Deref>(
622
644
log_trace ! ( logger, "Ignoring channel {} without enough capacity for invoice route hints" ,
623
645
log_bytes!( channel. channel_id) ) ;
624
646
} else {
625
- debug_assert ! ( !channel. is_usable) ;
647
+ debug_assert ! ( !channel. is_usable || ( has_pub_unconf_chan && !channel . is_public ) ) ;
626
648
log_trace ! ( logger, "Ignoring channel {} with disconnected peer" ,
627
649
log_bytes!( channel. channel_id) ) ;
628
650
}
@@ -792,6 +814,63 @@ mod test {
792
814
assert_eq ! ( invoice. payment_hash( ) , & sha256:: Hash :: from_slice( & payment_hash. 0 [ ..] ) . unwrap( ) ) ;
793
815
}
794
816
817
+ #[ test]
818
+ fn test_hints_has_only_public_confd_channels ( ) {
819
+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
820
+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
821
+ let mut config = test_default_channel_config ( ) ;
822
+ config. channel_handshake_config . minimum_depth = 1 ;
823
+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( config) , Some ( config) ] ) ;
824
+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
825
+
826
+ // Create a private channel with lots of capacity and a lower value public channel (without
827
+ // confirming the funding tx yet).
828
+ let unannounced_scid = create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 0 ) ;
829
+ let conf_tx = create_chan_between_nodes_with_value_init ( & nodes[ 0 ] , & nodes[ 1 ] , 10_000 , 0 ) ;
830
+
831
+ // Before the channel is available, we should include the unannounced_scid.
832
+ let mut scid_aliases = HashSet :: new ( ) ;
833
+ scid_aliases. insert ( unannounced_scid. 0 . short_channel_id_alias . unwrap ( ) ) ;
834
+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
835
+
836
+ // However after we mine the funding tx and exchange channel_ready messages for the public
837
+ // channel we'll immediately switch to including it as a route hint, even though isn't yet
838
+ // announced.
839
+ let pub_channel_scid = mine_transaction ( & nodes[ 0 ] , & conf_tx) ;
840
+ let node_a_pub_channel_ready = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendChannelReady , nodes[ 1 ] . node. get_our_node_id( ) ) ;
841
+ nodes[ 1 ] . node . handle_channel_ready ( & nodes[ 0 ] . node . get_our_node_id ( ) , & node_a_pub_channel_ready) ;
842
+
843
+ assert_eq ! ( mine_transaction( & nodes[ 1 ] , & conf_tx) , pub_channel_scid) ;
844
+ let events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
845
+ assert_eq ! ( events. len( ) , 2 ) ;
846
+ if let MessageSendEvent :: SendChannelReady { msg, .. } = & events[ 0 ] {
847
+ nodes[ 0 ] . node . handle_channel_ready ( & nodes[ 1 ] . node . get_our_node_id ( ) , msg) ;
848
+ } else { panic ! ( ) ; }
849
+ if let MessageSendEvent :: SendChannelUpdate { msg, .. } = & events[ 1 ] {
850
+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , msg) ;
851
+ } else { panic ! ( ) ; }
852
+
853
+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendChannelUpdate , nodes[ 1 ] . node. get_our_node_id( ) ) ) ;
854
+
855
+ expect_channel_ready_event ( & nodes[ 0 ] , & nodes[ 1 ] . node . get_our_node_id ( ) ) ;
856
+ expect_channel_ready_event ( & nodes[ 1 ] , & nodes[ 0 ] . node . get_our_node_id ( ) ) ;
857
+
858
+ scid_aliases. clear ( ) ;
859
+ scid_aliases. insert ( node_a_pub_channel_ready. short_channel_id_alias . unwrap ( ) ) ;
860
+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
861
+ // This also applies even if the amount is more than the payment amount, to ensure users
862
+ // dont screw up their privacy.
863
+ match_invoice_routes ( Some ( 50_000_000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
864
+
865
+ // The same remains true until the channel has 7 confirmations, at which point we include
866
+ // no hints.
867
+ connect_blocks ( & nodes[ 1 ] , 5 ) ;
868
+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
869
+ connect_blocks ( & nodes[ 1 ] , 1 ) ;
870
+ get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendAnnouncementSignatures , nodes[ 0 ] . node. get_our_node_id( ) ) ;
871
+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , HashSet :: new ( ) ) ;
872
+ }
873
+
795
874
#[ test]
796
875
fn test_hints_includes_single_channels_to_nodes ( ) {
797
876
let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
0 commit comments