@@ -1635,6 +1635,79 @@ impl<'a> NodeCounters<'a> {
1635
1635
}
1636
1636
}
1637
1637
1638
+ /// Calculates the introduction point for each blinded path in the given [`PaymentParameters`], if
1639
+ /// they can be found.
1640
+ fn calculate_blinded_path_intro_points < ' a , L : Deref > (
1641
+ payment_params : & PaymentParameters , node_counters : & ' a NodeCounters ,
1642
+ network_graph : & ReadOnlyNetworkGraph , logger : & L , our_node_id : NodeId ,
1643
+ first_hop_targets : & HashMap < NodeId , ( Vec < & ChannelDetails > , u32 ) > ,
1644
+ ) -> Result < Vec < Option < ( & ' a NodeId , u32 ) > > , LightningError >
1645
+ where L :: Target : Logger {
1646
+ let introduction_node_id_cache = payment_params. payee . blinded_route_hints ( ) . iter ( )
1647
+ . map ( |( _, path) | {
1648
+ match & path. introduction_node {
1649
+ IntroductionNode :: NodeId ( pubkey) => {
1650
+ // Note that this will only return `Some` if the `pubkey` is somehow known to
1651
+ // us (i.e. a channel counterparty or in the network graph).
1652
+ node_counters. node_counter_from_id ( & NodeId :: from_pubkey ( & pubkey) )
1653
+ } ,
1654
+ IntroductionNode :: DirectedShortChannelId ( direction, scid) => {
1655
+ path. public_introduction_node_id ( network_graph)
1656
+ . map ( |node_id_ref| * node_id_ref)
1657
+ . or_else ( || {
1658
+ first_hop_targets. iter ( ) . find ( |( _, ( channels, _) ) |
1659
+ channels
1660
+ . iter ( )
1661
+ . any ( |details| Some ( * scid) == details. get_outbound_payment_scid ( ) )
1662
+ ) . map ( |( cp, _) | direction. select_node_id ( our_node_id, * cp) )
1663
+ } )
1664
+ . and_then ( |node_id| node_counters. node_counter_from_id ( & node_id) )
1665
+ } ,
1666
+ }
1667
+ } )
1668
+ . collect :: < Vec < _ > > ( ) ;
1669
+ match & payment_params. payee {
1670
+ Payee :: Clear { route_hints, node_id, .. } => {
1671
+ for route in route_hints. iter ( ) {
1672
+ for hop in & route. 0 {
1673
+ if hop. src_node_id == * node_id {
1674
+ return Err ( LightningError {
1675
+ err : "Route hint cannot have the payee as the source." . to_owned ( ) ,
1676
+ action : ErrorAction :: IgnoreError
1677
+ } ) ;
1678
+ }
1679
+ }
1680
+ }
1681
+ } ,
1682
+ Payee :: Blinded { route_hints, .. } => {
1683
+ if introduction_node_id_cache. iter ( ) . all ( |info_opt| info_opt. map ( |( a, _) | a) == Some ( & our_node_id) ) {
1684
+ return Err ( LightningError { err : "Cannot generate a route to blinded paths if we are the introduction node to all of them" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
1685
+ }
1686
+ for ( ( _, blinded_path) , info_opt) in route_hints. iter ( ) . zip ( introduction_node_id_cache. iter ( ) ) {
1687
+ if blinded_path. blinded_hops . len ( ) == 0 {
1688
+ return Err ( LightningError { err : "0-hop blinded path provided" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
1689
+ }
1690
+ let introduction_node_id = match info_opt {
1691
+ None => continue ,
1692
+ Some ( info) => info. 0 ,
1693
+ } ;
1694
+ if * introduction_node_id == our_node_id {
1695
+ log_info ! ( logger, "Got blinded path with ourselves as the introduction node, ignoring" ) ;
1696
+ } else if blinded_path. blinded_hops . len ( ) == 1 &&
1697
+ route_hints
1698
+ . iter ( ) . zip ( introduction_node_id_cache. iter ( ) )
1699
+ . filter ( |( ( _, p) , _) | p. blinded_hops . len ( ) == 1 )
1700
+ . any ( |( _, iter_info_opt) | iter_info_opt. is_some ( ) && iter_info_opt != info_opt)
1701
+ {
1702
+ return Err ( LightningError { err : format ! ( "1-hop blinded paths must all have matching introduction node ids" ) , action : ErrorAction :: IgnoreError } ) ;
1703
+ }
1704
+ }
1705
+ }
1706
+ }
1707
+
1708
+ Ok ( introduction_node_id_cache)
1709
+ }
1710
+
1638
1711
#[ inline]
1639
1712
fn max_htlc_from_capacity ( capacity : EffectiveCapacity , max_channel_saturation_power_of_half : u8 ) -> u64 {
1640
1713
let saturation_shift: u32 = max_channel_saturation_power_of_half as u32 ;
@@ -2183,67 +2256,9 @@ where L::Target: Logger {
2183
2256
2184
2257
let node_counters = node_counter_builder. build ( ) ;
2185
2258
2186
- let introduction_node_id_cache = payment_params. payee . blinded_route_hints ( ) . iter ( )
2187
- . map ( |( _, path) | {
2188
- match & path. introduction_node {
2189
- IntroductionNode :: NodeId ( pubkey) => {
2190
- // Note that this will only return `Some` if the `pubkey` is somehow known to
2191
- // us (i.e. a channel counterparty or in the network graph).
2192
- node_counters. node_counter_from_id ( & NodeId :: from_pubkey ( & pubkey) )
2193
- } ,
2194
- IntroductionNode :: DirectedShortChannelId ( direction, scid) => {
2195
- path. public_introduction_node_id ( network_graph)
2196
- . map ( |node_id_ref| * node_id_ref)
2197
- . or_else ( || {
2198
- first_hop_targets. iter ( ) . find ( |( _, ( channels, _) ) |
2199
- channels
2200
- . iter ( )
2201
- . any ( |details| Some ( * scid) == details. get_outbound_payment_scid ( ) )
2202
- ) . map ( |( cp, _) | direction. select_node_id ( our_node_id, * cp) )
2203
- } )
2204
- . and_then ( |node_id| node_counters. node_counter_from_id ( & node_id) )
2205
- } ,
2206
- }
2207
- } )
2208
- . collect :: < Vec < _ > > ( ) ;
2209
- match & payment_params. payee {
2210
- Payee :: Clear { route_hints, node_id, .. } => {
2211
- for route in route_hints. iter ( ) {
2212
- for hop in & route. 0 {
2213
- if hop. src_node_id == * node_id {
2214
- return Err ( LightningError {
2215
- err : "Route hint cannot have the payee as the source." . to_owned ( ) ,
2216
- action : ErrorAction :: IgnoreError
2217
- } ) ;
2218
- }
2219
- }
2220
- }
2221
- } ,
2222
- Payee :: Blinded { route_hints, .. } => {
2223
- if introduction_node_id_cache. iter ( ) . all ( |info_opt| info_opt. map ( |( a, _) | a) == Some ( & our_node_id) ) {
2224
- return Err ( LightningError { err : "Cannot generate a route to blinded paths if we are the introduction node to all of them" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
2225
- }
2226
- for ( ( _, blinded_path) , info_opt) in route_hints. iter ( ) . zip ( introduction_node_id_cache. iter ( ) ) {
2227
- if blinded_path. blinded_hops . len ( ) == 0 {
2228
- return Err ( LightningError { err : "0-hop blinded path provided" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
2229
- }
2230
- let introduction_node_id = match info_opt {
2231
- None => continue ,
2232
- Some ( info) => info. 0 ,
2233
- } ;
2234
- if * introduction_node_id == our_node_id {
2235
- log_info ! ( logger, "Got blinded path with ourselves as the introduction node, ignoring" ) ;
2236
- } else if blinded_path. blinded_hops . len ( ) == 1 &&
2237
- route_hints
2238
- . iter ( ) . zip ( introduction_node_id_cache. iter ( ) )
2239
- . filter ( |( ( _, p) , _) | p. blinded_hops . len ( ) == 1 )
2240
- . any ( |( _, iter_info_opt) | iter_info_opt. is_some ( ) && iter_info_opt != info_opt)
2241
- {
2242
- return Err ( LightningError { err : format ! ( "1-hop blinded paths must all have matching introduction node ids" ) , action : ErrorAction :: IgnoreError } ) ;
2243
- }
2244
- }
2245
- }
2246
- }
2259
+ let introduction_node_id_cache = calculate_blinded_path_intro_points (
2260
+ & payment_params, & node_counters, network_graph, & logger, our_node_id, & first_hop_targets,
2261
+ ) ?;
2247
2262
2248
2263
// The main heap containing all candidate next-hops sorted by their score (max(fee,
2249
2264
// htlc_minimum)). Ideally this would be a heap which allowed cheap score reduction instead of
0 commit comments