@@ -63,7 +63,6 @@ impl ProbingStrategy for HighDegreeStrategy {
6363 let graph = self . network_graph . read_only ( ) ;
6464
6565 // Collect (pubkey, channel_count) for all nodes.
66- // wtf it does why we need to iterate here and then sort? maybe we can go just once?
6766 let mut nodes_by_degree: Vec < ( PublicKey , usize ) > = graph
6867 . nodes ( )
6968 . unordered_iter ( )
@@ -76,8 +75,6 @@ impl ProbingStrategy for HighDegreeStrategy {
7675 return None ;
7776 }
7877
79- // Most-connected first.
80- // wtf it does
8178 nodes_by_degree. sort_unstable_by ( |a, b| b. 1 . cmp ( & a. 1 ) ) ;
8279
8380 let top_n = self . top_n . min ( nodes_by_degree. len ( ) ) ;
@@ -137,37 +134,30 @@ impl RandomStrategy {
137134 /// Tries to build a path for the given cursor value and hop count. Returns `None` if the
138135 /// local node has no usable channels, or the walk terminates before reaching `target_hops`.
139136 fn try_build_path ( & self , cursor : usize , target_hops : usize , amount_msat : u64 ) -> Option < Path > {
140- // Collect confirmed, usable channels: (scid, peer_pubkey).
141- let our_channels: Vec < ( u64 , PublicKey ) > = self
142- . channel_manager
143- . list_channels ( )
144- . into_iter ( )
145- . filter_map ( |c| {
146- if c. is_usable {
147- c. short_channel_id . map ( |scid| ( scid, c. counterparty . node_id ) )
148- } else {
149- None
150- }
151- } )
152- . collect ( ) ;
137+ let initial_channels =
138+ self . channel_manager . list_channels ( ) . into_iter ( ) . filter ( |c|
139+ c. is_usable && c. short_channel_id . is_some ( ) ) . collect :: < Vec < _ > > ( ) ;
153140
154- if our_channels . is_empty ( ) {
141+ if initial_channels . is_empty ( ) {
155142 return None ;
156143 }
157144
158145 let graph = self . network_graph . read_only ( ) ;
159- let ( our_scid, peer_pubkey) = our_channels[ cursor % our_channels. len ( ) ] ;
160- let peer_node_id = NodeId :: from_pubkey ( & peer_pubkey) ;
146+ let first_hop = & initial_channels[ cursor % initial_channels. len ( ) ] ;
147+ let first_hop_scid = first_hop. short_channel_id . unwrap ( ) ;
148+ let next_peer_pubkey = first_hop. counterparty . node_id ;
149+ let next_peer_node_id = NodeId :: from_pubkey ( & next_peer_pubkey) ;
161150
162- // Walk the graph: each entry is (node_id, arrived_via_scid, pubkey).
163- // We start by having "arrived at peer via our_scid".
164- let mut visited: Vec < ( NodeId , u64 , PublicKey ) > =
165- vec ! [ ( peer_node_id, our_scid, peer_pubkey) ] ;
151+ // Track the tightest HTLC limit across all hops to cap the probe amount.
152+ // The first hop limit comes from our live channel state; subsequent hops use htlc_maximum_msat from the gossip channel update.
153+ let mut route_least_htlc_upper_bound = first_hop. next_outbound_htlc_limit_msat ;
166154
167- let mut prev_scid = our_scid;
168- let mut current_node_id = peer_node_id;
155+ // Walk the graph: each entry is (node_id, arrived_via_scid, pubkey); first entry is set:
156+ let mut route: Vec < ( NodeId , u64 , PublicKey ) > = vec ! [ ( next_peer_node_id, first_hop_scid, next_peer_pubkey) ] ;
157+
158+ let mut prev_scid = first_hop_scid;
159+ let mut current_node_id = next_peer_node_id;
169160
170- //wtf the real amount of hops is -1 of target?
171161 for hop_idx in 1 ..target_hops {
172162 let node_info = match graph. node ( & current_node_id) {
173163 Some ( n) => n,
@@ -187,46 +177,48 @@ impl RandomStrategy {
187177 None => break ,
188178 } ;
189179
190- // Determine direction and fetch the channel-update.
191- let ( update, next_node_id) = if next_channel. node_one == current_node_id {
192- match next_channel. one_to_two . as_ref ( ) {
193- Some ( u) => ( u, next_channel. node_two ) ,
194- None => break ,
195- }
196- } else if next_channel. node_two == current_node_id {
197- match next_channel. two_to_one . as_ref ( ) {
198- Some ( u) => ( u, next_channel. node_one ) ,
199- None => break ,
200- }
201- } else {
180+ // as_directed_from validates that current_node_id is a channel endpoint and that
181+ // both direction updates are present; effective_capacity covers both htlc_maximum_msat
182+ // and funding capacity.
183+ let Some ( ( directed, next_node_id) ) = next_channel. as_directed_from ( & current_node_id)
184+ else {
202185 break ;
203186 } ;
187+ // Retrieve the direction-specific update via the public ChannelInfo fields.
188+ // Safe to unwrap: as_directed_from already checked both directions are Some.
189+ let update = if directed. source ( ) == & next_channel. node_one {
190+ next_channel. one_to_two . as_ref ( ) . unwrap ( )
191+ } else {
192+ next_channel. two_to_one . as_ref ( ) . unwrap ( )
193+ } ;
204194
205195 if !update. enabled {
206196 break ;
207197 }
208198
209- let next_pubkey = match PublicKey :: try_from ( next_node_id) {
199+ route_least_htlc_upper_bound = route_least_htlc_upper_bound. min ( update. htlc_maximum_msat ) ;
200+
201+ let next_pubkey = match PublicKey :: try_from ( * next_node_id) {
210202 Ok ( pk) => pk,
211203 Err ( _) => break ,
212204 } ;
213205
214- visited . push ( ( next_node_id, next_scid, next_pubkey) ) ;
206+ route . push ( ( * next_node_id, next_scid, next_pubkey) ) ;
215207 prev_scid = next_scid;
216- current_node_id = next_node_id;
208+ current_node_id = * next_node_id;
217209 }
218210
219- // Require the full requested depth; shorter walks are uninformative.
220- if visited . len ( ) < target_hops {
211+ let amount_msat = amount_msat . min ( route_least_htlc_upper_bound ) ; //cap probe amount
212+ if amount_msat < self . min_amount_msat {
221213 return None ;
222214 }
223215
224216 // Assemble hops.
225- // For hop i: fee and CLTV are determined by the *next* channel (what visited [i]
217+ // For hop i: fee and CLTV are determined by the *next* channel (what route [i]
226218 // will charge to forward onward). For the last hop they are amount_msat and zero expiry delta.
227- let mut hops = Vec :: with_capacity ( visited . len ( ) ) ;
228- for i in 0 ..visited . len ( ) {
229- let ( node_id, via_scid, pubkey) = visited [ i] ;
219+ let mut hops = Vec :: with_capacity ( route . len ( ) ) ;
220+ for i in 0 ..route . len ( ) {
221+ let ( node_id, via_scid, pubkey) = route [ i] ;
230222
231223 let channel_info = graph. channel ( via_scid) ?;
232224
@@ -235,22 +227,22 @@ impl RandomStrategy {
235227 . and_then ( |n| n. announcement_info . as_ref ( ) . map ( |a| a. features ( ) . clone ( ) ) )
236228 . unwrap_or_else ( NodeFeatures :: empty) ;
237229
238- let ( fee_msat, cltv_expiry_delta) = if i + 1 < visited. len ( ) {
239- // Intermediate hop: look up the next channel's update from node_id.
240- let ( _next_node_id, next_scid, _) = visited[ i + 1 ] ;
241- let next_channel = graph. channel ( next_scid) ?;
242- let update = if next_channel. node_one == node_id {
243- next_channel. one_to_two . as_ref ( ) ?
230+ let ( fee_msat, cltv_expiry_delta) =
231+ if i + 1 < route. len ( ) { // non-final hop
232+ let ( _, next_scid, _) = route[ i + 1 ] ;
233+ let next_channel = graph. channel ( next_scid) ?;
234+ let ( directed, _) = next_channel. as_directed_from ( & node_id) ?;
235+ let update = if directed. source ( ) == & next_channel. node_one {
236+ next_channel. one_to_two . as_ref ( ) . unwrap ( )
237+ } else {
238+ next_channel. two_to_one . as_ref ( ) . unwrap ( )
239+ } ;
240+ let fee = update. fees . base_msat as u64 + ( amount_msat * update. fees . proportional_millionths as u64 / 1_000_000 ) ;
241+ ( fee, update. cltv_expiry_delta as u32 )
244242 } else {
245- next_channel. two_to_one . as_ref ( ) ?
243+ // Final hop: fee_msat carries the delivery amount; cltv delta is zero.
244+ ( amount_msat, 0 )
246245 } ;
247- let fee = update. fees . base_msat as u64
248- + ( amount_msat * update. fees . proportional_millionths as u64 / 1_000_000 ) ;
249- ( fee, update. cltv_expiry_delta as u32 )
250- } else {
251- // Final hop.
252- ( amount_msat, 0 )
253- } ;
254246
255247 hops. push ( RouteHop {
256248 pubkey,
@@ -263,6 +255,13 @@ impl RandomStrategy {
263255 } ) ;
264256 }
265257
258+ // The first-hop HTLC carries amount_msat + all intermediate fees.
259+ // Verify the total fits within our live outbound limit before returning.
260+ let total_outgoing: u64 = hops. iter ( ) . map ( |h| h. fee_msat ) . sum ( ) ;
261+ if total_outgoing > first_hop. next_outbound_htlc_limit_msat {
262+ return None ;
263+ }
264+
266265 Some ( Path { hops, blinded_tail : None } )
267266 }
268267}
0 commit comments