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