@@ -184,11 +184,38 @@ pub(super) fn compute_payinfo(
184184
185185		cltv_expiry_delta = cltv_expiry_delta. checked_add ( tlvs. payment_relay . cltv_expiry_delta ) . ok_or ( ( ) ) ?; 
186186	} 
187+ 
188+ 	let  mut  htlc_minimum_msat = 0 ; 
189+ 	for  ( idx,  node)  in  intermediate_nodes. iter ( ) . map ( |( _,  tlvs) | tlvs) . enumerate ( )  { 
190+ 		let  mut  htlc_min_candidate = node. payment_constraints . htlc_minimum_msat  as  u128 ; 
191+ 		// Get an iterator over `[curr_hop_tlvs..last_intermediate_hop_tlvs]`. 
192+ 		let  next_nodes = intermediate_nodes. iter ( ) 
193+ 			. enumerate ( ) 
194+ 			. skip_while ( |( i,  _) | * i != idx) 
195+ 			. map ( |( _,  ( _,  tlvs) ) | tlvs) ; 
196+ 		for  node in  next_nodes { 
197+ 			// The min htlc for a hop is that hop's htlc_minimum_msat minus the fees paid from that hop to 
198+ 			// the end of the path, because the sender will automatically include that following fee 
199+ 			// amount in the amount that this hop receives. 
200+ 			let  prop_fee = node. payment_relay . fee_proportional_millionths  as  u128 ; 
201+ 			let  base_fee = node. payment_relay . fee_base_msat  as  u128 ; 
202+ 			let  hop_fee = htlc_min_candidate
203+ 				. checked_mul ( prop_fee) 
204+ 				. and_then ( |prop_fee| ( prop_fee / 1_000_000 ) . checked_add ( base_fee) ) 
205+ 				. ok_or ( ( ) ) ?; 
206+ 			htlc_min_candidate = htlc_min_candidate. saturating_sub ( hop_fee) ; 
207+ 			if  htlc_min_candidate == 0  {  break  } 
208+ 		} 
209+ 		htlc_minimum_msat = core:: cmp:: max ( htlc_min_candidate,  htlc_minimum_msat) ; 
210+ 	} 
211+ 	htlc_minimum_msat =
212+ 		core:: cmp:: max ( payee_tlvs. payment_constraints . htlc_minimum_msat  as  u128 ,  htlc_minimum_msat) ; 
213+ 
187214	Ok ( BlindedPayInfo  { 
188215		fee_base_msat :  u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?, 
189216		fee_proportional_millionths :  u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?, 
190217		cltv_expiry_delta, 
191- 		htlc_minimum_msat :  1 ,   // TODO 
218+ 		htlc_minimum_msat :  u64 :: try_from ( htlc_minimum_msat ) . map_err ( |_|  ( ) ) ? , 
192219		htlc_maximum_msat :  21_000_000  *  100_000_000  *  1_000 ,  // TODO 
193220		features :  BlindedHopFeatures :: empty ( ) , 
194221	} ) 
@@ -253,6 +280,7 @@ mod tests {
253280		assert_eq ! ( blinded_payinfo. fee_base_msat,  201 ) ; 
254281		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  1001 ) ; 
255282		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  288 ) ; 
283+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  900 ) ; 
256284	} 
257285
258286	#[ test]  
@@ -268,5 +296,89 @@ mod tests {
268296		assert_eq ! ( blinded_payinfo. fee_base_msat,  0 ) ; 
269297		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  0 ) ; 
270298		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  0 ) ; 
299+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  1 ) ; 
300+ 	} 
301+ 
302+ 	#[ test]  
303+ 	fn  simple_aggregated_htlc_min ( )  { 
304+ 		// If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat 
305+ 		// along the path. 
306+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
307+ 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
308+ 			short_channel_id:  0 , 
309+ 			payment_relay:  PaymentRelay  { 
310+ 				cltv_expiry_delta:  0 , 
311+ 				fee_proportional_millionths:  0 , 
312+ 				fee_base_msat:  0 , 
313+ 			} , 
314+ 			payment_constraints:  PaymentConstraints  { 
315+ 				max_cltv_expiry:  0 , 
316+ 				htlc_minimum_msat:  1 , 
317+ 			} , 
318+ 			features:  BlindedHopFeatures :: empty( ) , 
319+ 		} ) ,  ( dummy_pk,  ForwardTlvs  { 
320+ 			short_channel_id:  0 , 
321+ 			payment_relay:  PaymentRelay  { 
322+ 				cltv_expiry_delta:  0 , 
323+ 				fee_proportional_millionths:  0 , 
324+ 				fee_base_msat:  0 , 
325+ 			} , 
326+ 			payment_constraints:  PaymentConstraints  { 
327+ 				max_cltv_expiry:  0 , 
328+ 				htlc_minimum_msat:  2_000 , 
329+ 			} , 
330+ 			features:  BlindedHopFeatures :: empty( ) , 
331+ 		} ) ] ; 
332+ 		let  recv_tlvs = ReceiveTlvs  { 
333+ 			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
334+ 			payment_constraints :  PaymentConstraints  { 
335+ 				max_cltv_expiry :  0 , 
336+ 				htlc_minimum_msat :  3 , 
337+ 			} , 
338+ 		} ; 
339+ 		let  blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] ,  & recv_tlvs) . unwrap ( ) ; 
340+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  2_000 ) ; 
341+ 	} 
342+ 
343+ 	#[ test]  
344+ 	fn  aggregated_htlc_min ( )  { 
345+ 		// Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the 
346+ 		// max (htlc_min - following_fees) along the path. 
347+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
348+ 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
349+ 			short_channel_id:  0 , 
350+ 			payment_relay:  PaymentRelay  { 
351+ 				cltv_expiry_delta:  0 , 
352+ 				fee_proportional_millionths:  500 , 
353+ 				fee_base_msat:  1_000 , 
354+ 			} , 
355+ 			payment_constraints:  PaymentConstraints  { 
356+ 				max_cltv_expiry:  0 , 
357+ 				htlc_minimum_msat:  5_000 , 
358+ 			} , 
359+ 			features:  BlindedHopFeatures :: empty( ) , 
360+ 		} ) ,  ( dummy_pk,  ForwardTlvs  { 
361+ 			short_channel_id:  0 , 
362+ 			payment_relay:  PaymentRelay  { 
363+ 				cltv_expiry_delta:  0 , 
364+ 				fee_proportional_millionths:  500 , 
365+ 				fee_base_msat:  200 , 
366+ 			} , 
367+ 			payment_constraints:  PaymentConstraints  { 
368+ 				max_cltv_expiry:  0 , 
369+ 				htlc_minimum_msat:  2_000 , 
370+ 			} , 
371+ 			features:  BlindedHopFeatures :: empty( ) , 
372+ 		} ) ] ; 
373+ 		let  recv_tlvs = ReceiveTlvs  { 
374+ 			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
375+ 			payment_constraints :  PaymentConstraints  { 
376+ 				max_cltv_expiry :  0 , 
377+ 				htlc_minimum_msat :  1 , 
378+ 			} , 
379+ 		} ; 
380+ 
381+ 		let  blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] ,  & recv_tlvs) . unwrap ( ) ; 
382+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  3797 ) ; 
271383	} 
272384} 
0 commit comments