@@ -512,8 +512,10 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
512512/// * Always select the channel with the highest inbound capacity per counterparty node
513513/// * Prefer channels with capacity at least `min_inbound_capacity_msat` and where the channel
514514/// `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.
517519fn filter_channels < L : Deref > (
518520 channels : Vec < ChannelDetails > , min_inbound_capacity_msat : Option < u64 > , logger : & L
519521) -> Vec < RouteHint > where L :: Target : Logger {
@@ -522,6 +524,7 @@ fn filter_channels<L: Deref>(
522524 let mut min_capacity_channel_exists = false ;
523525 let mut online_channel_exists = false ;
524526 let mut online_min_capacity_channel_exists = false ;
527+ let mut has_pub_unconf_chan = false ;
525528
526529 log_trace ! ( logger, "Considering {} channels for invoice route hints" , channels. len( ) ) ;
527530 for channel in channels. into_iter ( ) . filter ( |chan| chan. is_channel_ready ) {
@@ -531,11 +534,18 @@ fn filter_channels<L: Deref>(
531534 }
532535
533536 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+ }
539549 }
540550
541551 if channel. inbound_capacity_msat >= min_inbound_capacity {
@@ -557,20 +567,32 @@ fn filter_channels<L: Deref>(
557567 match filtered_channels. entry ( channel. counterparty . node_id ) {
558568 hash_map:: Entry :: Occupied ( mut entry) => {
559569 let current_max_capacity = entry. get ( ) . inbound_capacity_msat ;
560- if channel. inbound_capacity_msat < current_max_capacity {
570+ // If this channel is public and the previous channel is not, ensure we replace the
571+ // previous channel to avoid announcing non-public channels.
572+ let new_now_public = channel. is_public && !entry. get ( ) . is_public ;
573+ // If the public-ness of the channel has not changed (in which case simply defer to
574+ // `new_now_public), and this channel has a greater capacity, prefer to announce
575+ // this channel.
576+ let new_higher_capacity = channel. is_public == entry. get ( ) . is_public &&
577+ channel. inbound_capacity_msat > current_max_capacity;
578+ if new_now_public || new_higher_capacity {
579+ log_trace ! ( logger,
580+ "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints" ,
581+ log_pubkey!( channel. counterparty. node_id) ,
582+ log_bytes!( channel. channel_id) , channel. short_channel_id,
583+ channel. inbound_capacity_msat,
584+ log_bytes!( entry. get( ) . channel_id) , entry. get( ) . short_channel_id,
585+ current_max_capacity) ;
586+ entry. insert ( channel) ;
587+ } else {
561588 log_trace ! ( logger,
562- "Preferring counterparty {} channel {} ({} msats) over {} ({} msats) for invoice route hints" ,
589+ "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints" ,
563590 log_pubkey!( channel. counterparty. node_id) ,
564- log_bytes!( entry. get( ) . channel_id) , current_max_capacity,
565- log_bytes!( channel. channel_id) , channel. inbound_capacity_msat) ;
566- continue ;
591+ log_bytes!( entry. get( ) . channel_id) , entry. get( ) . short_channel_id,
592+ current_max_capacity,
593+ log_bytes!( channel. channel_id) , channel. short_channel_id,
594+ channel. inbound_capacity_msat) ;
567595 }
568- log_trace ! ( logger,
569- "Preferring counterparty {} channel {} ({} msats) over {} ({} msats) for invoice route hints" ,
570- log_pubkey!( channel. counterparty. node_id) ,
571- log_bytes!( channel. channel_id) , channel. inbound_capacity_msat,
572- log_bytes!( entry. get( ) . channel_id) , current_max_capacity) ;
573- entry. insert ( channel) ;
574596 }
575597 hash_map:: Entry :: Vacant ( entry) => {
576598 entry. insert ( channel) ;
@@ -600,7 +622,12 @@ fn filter_channels<L: Deref>(
600622 . map ( |( _, channel) | channel)
601623 . filter ( |channel| {
602624 let has_enough_capacity = channel. inbound_capacity_msat >= min_inbound_capacity;
603- let include_channel = if online_min_capacity_channel_exists {
625+ let include_channel = if has_pub_unconf_chan {
626+ // If we have a public channel, but it doesn't have enough confirmations to (yet)
627+ // be in the public network graph (and have gotten a chance to propagate), include
628+ // route hints but only for public channels to protect private channel privacy.
629+ channel. is_public
630+ } else if online_min_capacity_channel_exists {
604631 has_enough_capacity && channel. is_usable
605632 } else if min_capacity_channel_exists && online_channel_exists {
606633 // If there are some online channels and some min_capacity channels, but no
@@ -620,7 +647,7 @@ fn filter_channels<L: Deref>(
620647 log_trace ! ( logger, "Ignoring channel {} without enough capacity for invoice route hints" ,
621648 log_bytes!( channel. channel_id) ) ;
622649 } else {
623- debug_assert ! ( !channel. is_usable) ;
650+ debug_assert ! ( !channel. is_usable || ( has_pub_unconf_chan && !channel . is_public ) ) ;
624651 log_trace ! ( logger, "Ignoring channel {} with disconnected peer" ,
625652 log_bytes!( channel. channel_id) ) ;
626653 }
@@ -789,6 +816,63 @@ mod test {
789816 assert_eq ! ( invoice. payment_hash( ) , & sha256:: Hash :: from_slice( & payment_hash. 0 [ ..] ) . unwrap( ) ) ;
790817 }
791818
819+ #[ test]
820+ fn test_hints_has_only_public_confd_channels ( ) {
821+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
822+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
823+ let mut config = test_default_channel_config ( ) ;
824+ config. channel_handshake_config . minimum_depth = 1 ;
825+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( config) , Some ( config) ] ) ;
826+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
827+
828+ // Create a private channel with lots of capacity and a lower value public channel (without
829+ // confirming the funding tx yet).
830+ let unannounced_scid = create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 0 ) ;
831+ let conf_tx = create_chan_between_nodes_with_value_init ( & nodes[ 0 ] , & nodes[ 1 ] , 10_000 , 0 ) ;
832+
833+ // Before the channel is available, we should include the unannounced_scid.
834+ let mut scid_aliases = HashSet :: new ( ) ;
835+ scid_aliases. insert ( unannounced_scid. 0 . short_channel_id_alias . unwrap ( ) ) ;
836+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
837+
838+ // However after we mine the funding tx and exchange channel_ready messages for the public
839+ // channel we'll immediately switch to including it as a route hint, even though it isn't
840+ // yet announced.
841+ let pub_channel_scid = mine_transaction ( & nodes[ 0 ] , & conf_tx) ;
842+ let node_a_pub_channel_ready = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendChannelReady , nodes[ 1 ] . node. get_our_node_id( ) ) ;
843+ nodes[ 1 ] . node . handle_channel_ready ( & nodes[ 0 ] . node . get_our_node_id ( ) , & node_a_pub_channel_ready) ;
844+
845+ assert_eq ! ( mine_transaction( & nodes[ 1 ] , & conf_tx) , pub_channel_scid) ;
846+ let events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
847+ assert_eq ! ( events. len( ) , 2 ) ;
848+ if let MessageSendEvent :: SendChannelReady { msg, .. } = & events[ 0 ] {
849+ nodes[ 0 ] . node . handle_channel_ready ( & nodes[ 1 ] . node . get_our_node_id ( ) , msg) ;
850+ } else { panic ! ( ) ; }
851+ if let MessageSendEvent :: SendChannelUpdate { msg, .. } = & events[ 1 ] {
852+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , msg) ;
853+ } else { panic ! ( ) ; }
854+
855+ 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( ) ) ) ;
856+
857+ expect_channel_ready_event ( & nodes[ 0 ] , & nodes[ 1 ] . node . get_our_node_id ( ) ) ;
858+ expect_channel_ready_event ( & nodes[ 1 ] , & nodes[ 0 ] . node . get_our_node_id ( ) ) ;
859+
860+ scid_aliases. clear ( ) ;
861+ scid_aliases. insert ( node_a_pub_channel_ready. short_channel_id_alias . unwrap ( ) ) ;
862+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
863+ // This also applies even if the amount is more than the payment amount, to ensure users
864+ // dont screw up their privacy.
865+ match_invoice_routes ( Some ( 50_000_000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
866+
867+ // The same remains true until the channel has 7 confirmations, at which point we include
868+ // no hints.
869+ connect_blocks ( & nodes[ 1 ] , 5 ) ;
870+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , scid_aliases. clone ( ) ) ;
871+ connect_blocks ( & nodes[ 1 ] , 1 ) ;
872+ get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendAnnouncementSignatures , nodes[ 0 ] . node. get_our_node_id( ) ) ;
873+ match_invoice_routes ( Some ( 5000 ) , & nodes[ 1 ] , HashSet :: new ( ) ) ;
874+ }
875+
792876 #[ test]
793877 fn test_hints_includes_single_channels_to_nodes ( ) {
794878 let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
0 commit comments