@@ -1486,6 +1486,82 @@ enum CandidateHopId {
1486
1486
Blinded ( usize ) ,
1487
1487
}
1488
1488
1489
+ /// To avoid doing [`PublicKey`] -> [`PathBuildingHop`] hashtable lookups, we assign each
1490
+ /// [`PublicKey`]/node a `usize` index and simply keep a `Vec` of values.
1491
+ ///
1492
+ /// While this is easy for gossip-originating nodes (the [`DirectedChannelInfo`] exposes "counters"
1493
+ /// for us for this purpose) we have to have our own indexes for nodes originating from invoice
1494
+ /// hints, local channels, or blinded path fake nodes.
1495
+ ///
1496
+ /// This wrapper handles all this for us, allowing look-up of counters from the various contexts.
1497
+ ///
1498
+ /// It is first built by passing all [`NodeId`]s that we'll ever care about either though
1499
+ /// [`NodeCountersBuilder::node_counter_from_pubkey`] or
1500
+ /// [`NodeCountersBuilder::node_counter_from_id`], then calling [`NodeCountersBuilder::build`] and
1501
+ /// using the resulting [`NodeCounters`] to look up any counters.
1502
+ ///
1503
+ /// [`NodeCounters::private_node_counter_from_pubkey`], specifically, will return `Some` iff
1504
+ /// [`NodeCountersBuilder::node_counter_from_pubkey`] was called on the same key (not
1505
+ /// [`NodeCountersBuilder::node_counter_from_id`]). It will also returned a cached copy of the
1506
+ /// [`PublicKey`] -> [`NodeId`] conversion.
1507
+ struct NodeCounters < ' a > {
1508
+ network_graph : & ' a ReadOnlyNetworkGraph < ' a > ,
1509
+ private_node_id_to_node_counter : HashMap < NodeId , u32 > ,
1510
+ private_hop_key_cache : HashMap < PublicKey , ( NodeId , u32 ) > ,
1511
+ }
1512
+
1513
+ struct NodeCountersBuilder < ' a > ( NodeCounters < ' a > ) ;
1514
+
1515
+ impl < ' a > NodeCountersBuilder < ' a > {
1516
+ fn new ( network_graph : & ' a ReadOnlyNetworkGraph ) -> Self {
1517
+ Self ( NodeCounters {
1518
+ network_graph,
1519
+ private_node_id_to_node_counter : new_hash_map ( ) ,
1520
+ private_hop_key_cache : new_hash_map ( ) ,
1521
+ } )
1522
+ }
1523
+
1524
+ fn node_counter_from_pubkey ( & mut self , pubkey : PublicKey ) -> u32 {
1525
+ let id = NodeId :: from_pubkey ( & pubkey) ;
1526
+ let counter = self . node_counter_from_id ( id) ;
1527
+ self . 0 . private_hop_key_cache . insert ( pubkey, ( id, counter) ) ;
1528
+ counter
1529
+ }
1530
+
1531
+ fn node_counter_from_id ( & mut self , node_id : NodeId ) -> u32 {
1532
+ // For any node_id, we first have to check if its in the existing network graph, and then
1533
+ // ensure that we always look up in our internal map first.
1534
+ self . 0 . network_graph . nodes ( ) . get ( & node_id)
1535
+ . map ( |node| node. node_counter )
1536
+ . unwrap_or_else ( || {
1537
+ let next_node_counter = self . 0 . network_graph . max_node_counter ( ) + 1 +
1538
+ self . 0 . private_node_id_to_node_counter . len ( ) as u32 ;
1539
+ * self . 0 . private_node_id_to_node_counter . entry ( node_id) . or_insert ( next_node_counter)
1540
+ } )
1541
+ }
1542
+
1543
+ fn build ( self ) -> NodeCounters < ' a > { self . 0 }
1544
+ }
1545
+
1546
+ impl < ' a > NodeCounters < ' a > {
1547
+ fn max_counter ( & self ) -> u32 {
1548
+ self . network_graph . max_node_counter ( ) +
1549
+ self . private_node_id_to_node_counter . len ( ) as u32
1550
+ }
1551
+
1552
+ fn private_node_counter_from_pubkey ( & self , pubkey : & PublicKey ) -> Option < & ( NodeId , u32 ) > {
1553
+ self . private_hop_key_cache . get ( pubkey)
1554
+ }
1555
+
1556
+ fn node_counter_from_id ( & self , node_id : & NodeId ) -> Option < ( & NodeId , u32 ) > {
1557
+ self . private_node_id_to_node_counter . get_key_value ( node_id) . map ( |( a, b) | ( a, * b) )
1558
+ . or_else ( || {
1559
+ self . network_graph . nodes ( ) . get_key_value ( node_id)
1560
+ . map ( |( node_id, node) | ( node_id, node. node_counter ) )
1561
+ } )
1562
+ }
1563
+ }
1564
+
1489
1565
#[ inline]
1490
1566
fn max_htlc_from_capacity ( capacity : EffectiveCapacity , max_channel_saturation_power_of_half : u8 ) -> u64 {
1491
1567
let saturation_shift: u32 = max_channel_saturation_power_of_half as u32 ;
@@ -2038,6 +2114,17 @@ where L::Target: Logger {
2038
2114
}
2039
2115
}
2040
2116
2117
+ let mut node_counters = NodeCountersBuilder :: new ( & network_graph) ;
2118
+
2119
+ let payer_node_counter = node_counters. node_counter_from_pubkey ( * our_node_pubkey) ;
2120
+ let payee_node_counter = node_counters. node_counter_from_pubkey ( maybe_dummy_payee_pk) ;
2121
+
2122
+ for route in payment_params. payee . unblinded_route_hints ( ) . iter ( ) {
2123
+ for hop in route. 0 . iter ( ) {
2124
+ node_counters. node_counter_from_pubkey ( hop. src_node_id ) ;
2125
+ }
2126
+ }
2127
+
2041
2128
// Step (1).
2042
2129
// Prepare the data we'll use for payee-to-payer search by
2043
2130
// inserting first hops suggested by the caller as targets.
@@ -2052,9 +2139,14 @@ where L::Target: Logger {
2052
2139
if chan. counterparty . node_id == * our_node_pubkey {
2053
2140
return Err ( LightningError { err : "First hop cannot have our_node_pubkey as a destination." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
2054
2141
}
2142
+ let counterparty_id = NodeId :: from_pubkey ( & chan. counterparty . node_id ) ;
2055
2143
first_hop_targets
2056
- . entry ( NodeId :: from_pubkey ( & chan. counterparty . node_id ) )
2057
- . or_insert ( Vec :: new ( ) )
2144
+ . entry ( counterparty_id)
2145
+ . or_insert_with ( || {
2146
+ // Make sure there's a counter assigned for the counterparty
2147
+ node_counters. node_counter_from_id ( counterparty_id) ;
2148
+ Vec :: new ( )
2149
+ } )
2058
2150
. push ( chan) ;
2059
2151
}
2060
2152
if first_hop_targets. is_empty ( ) {
@@ -2076,6 +2168,8 @@ where L::Target: Logger {
2076
2168
}
2077
2169
}
2078
2170
2171
+ let node_counters = node_counters. build ( ) ;
2172
+
2079
2173
// The main heap containing all candidate next-hops sorted by their score (max(fee,
2080
2174
// htlc_minimum)). Ideally this would be a heap which allowed cheap score reduction instead of
2081
2175
// adding duplicate entries when we find a better path to a given node.
0 commit comments