@@ -16,6 +16,18 @@ use crate::util::ser::{Readable, Writeable, Writer};
1616
1717use  core:: convert:: TryFrom ; 
1818
19+ /// An intermediate node, its outbound channel, and relay parameters. 
20+ #[ derive( Clone ,  Debug ) ]  
21+ pub  struct  ForwardNode  { 
22+ 	/// The TLVs for this node's [`BlindedHop`], where the fee parameters contained within are also 
23+ /// used for [`BlindedPayInfo`] construction. 
24+ pub  tlvs :  ForwardTlvs , 
25+ 	/// This node's pubkey. 
26+ pub  node_id :  PublicKey , 
27+ 	/// The maximum value, in msat, that may be accepted by this node. 
28+ pub  htlc_maximum_msat :  u64 , 
29+ } 
30+ 
1931/// Data to construct a [`BlindedHop`] for forwarding a payment. 
2032#[ derive( Clone ,  Debug ) ]  
2133pub  struct  ForwardTlvs  { 
@@ -150,12 +162,12 @@ impl Readable for BlindedPaymentTlvs {
150162
151163/// Construct blinded payment hops for the given `intermediate_nodes` and payee info. 
152164pub ( super )  fn  blinded_hops < T :  secp256k1:: Signing  + secp256k1:: Verification > ( 
153- 	secp_ctx :  & Secp256k1 < T > ,  intermediate_nodes :  & [ ( PublicKey ,   ForwardTlvs ,   u64 ) ] , 
165+ 	secp_ctx :  & Secp256k1 < T > ,  intermediate_nodes :  & [ ForwardNode ] , 
154166	payee_node_id :  PublicKey ,  payee_tlvs :  ReceiveTlvs ,  session_priv :  & SecretKey 
155167)  -> Result < Vec < BlindedHop > ,  secp256k1:: Error >  { 
156- 	let  pks = intermediate_nodes. iter ( ) . map ( |( pk ,  _ ,  _ ) | pk ) 
168+ 	let  pks = intermediate_nodes. iter ( ) . map ( |node|  & node . node_id ) 
157169		. chain ( core:: iter:: once ( & payee_node_id) ) ; 
158- 	let  tlvs = intermediate_nodes. iter ( ) . map ( |( _ ,  tlvs ,  _ ) | BlindedPaymentTlvsRef :: Forward ( tlvs) ) 
170+ 	let  tlvs = intermediate_nodes. iter ( ) . map ( |node | BlindedPaymentTlvsRef :: Forward ( & node . tlvs ) ) 
159171		. chain ( core:: iter:: once ( BlindedPaymentTlvsRef :: Receive ( & payee_tlvs) ) ) ; 
160172	utils:: construct_blinded_hops ( secp_ctx,  pks,  tlvs,  session_priv) 
161173} 
@@ -182,13 +194,12 @@ fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> O
182194} 
183195
184196pub ( super )  fn  compute_payinfo ( 
185- 	intermediate_nodes :  & [ ( PublicKey ,  ForwardTlvs ,  u64 ) ] ,  payee_tlvs :  & ReceiveTlvs , 
186- 	payee_htlc_maximum_msat :  u64 
197+ 	intermediate_nodes :  & [ ForwardNode ] ,  payee_tlvs :  & ReceiveTlvs ,  payee_htlc_maximum_msat :  u64 
187198)  -> Result < BlindedPayInfo ,  ( ) >  { 
188199	let  mut  curr_base_fee:  u64  = 0 ; 
189200	let  mut  curr_prop_mil:  u64  = 0 ; 
190201	let  mut  cltv_expiry_delta:  u16  = 0 ; 
191- 	for  ( _ ,   tlvs,  _ )   in  intermediate_nodes. iter ( ) . rev ( )  { 
202+ 	for  tlvs  in  intermediate_nodes. iter ( ) . rev ( ) . map ( |n|  & n . tlvs )  { 
192203		// In the future, we'll want to take the intersection of all supported features for the 
193204		// `BlindedPayInfo`, but there are no features in that context right now. 
194205		if  tlvs. features . requires_unknown_bits_from ( & BlindedHopFeatures :: empty ( ) )  {  return  Err ( ( ) )  } 
@@ -216,16 +227,16 @@ pub(super) fn compute_payinfo(
216227
217228	let  mut  htlc_minimum_msat:  u64  = 1 ; 
218229	let  mut  htlc_maximum_msat:  u64  = 21_000_000  *  100_000_000  *  1_000 ;  // Total bitcoin supply 
219- 	for  ( _ ,  tlvs ,  max_htlc_candidate )  in  intermediate_nodes. iter ( )  { 
230+ 	for  node  in  intermediate_nodes. iter ( )  { 
220231		// The min htlc for an intermediate node is that node's min minus the fees charged by all of the 
221232		// following hops for forwarding that min, since that fee amount will automatically be included 
222233		// in the amount that this node receives and contribute towards reaching its min. 
223234		htlc_minimum_msat = amt_to_forward_msat ( 
224- 			core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat ,  htlc_minimum_msat) , 
225- 			& tlvs. payment_relay 
235+ 			core:: cmp:: max ( node . tlvs . payment_constraints . htlc_minimum_msat ,  htlc_minimum_msat) , 
236+ 			& node . tlvs . payment_relay 
226237		) . unwrap_or ( 1 ) ;  // If underflow occurs, we definitely reached this node's min 
227238		htlc_maximum_msat = amt_to_forward_msat ( 
228- 			core:: cmp:: min ( * max_htlc_candidate ,  htlc_maximum_msat) ,  & tlvs. payment_relay 
239+ 			core:: cmp:: min ( node . htlc_maximum_msat ,  htlc_maximum_msat) ,  & node . tlvs . payment_relay 
229240		) . ok_or ( ( ) ) ?;  // If underflow occurs, we cannot send to this hop without exceeding their max 
230241	} 
231242	htlc_minimum_msat = core:: cmp:: max ( 
@@ -258,7 +269,7 @@ impl_writeable_msg!(PaymentConstraints, {
258269#[ cfg( test) ]  
259270mod  tests { 
260271	use  bitcoin:: secp256k1:: PublicKey ; 
261- 	use  crate :: blinded_path:: payment:: { ForwardTlvs ,  ReceiveTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
272+ 	use  crate :: blinded_path:: payment:: { ForwardNode ,   ForwardTlvs ,  ReceiveTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
262273	use  crate :: ln:: PaymentSecret ; 
263274	use  crate :: ln:: features:: BlindedHopFeatures ; 
264275
@@ -267,31 +278,39 @@ mod tests {
267278		// Taken from the spec example for aggregating blinded payment info. See 
268279		// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md#blinded-payments 
269280		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
270- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
271- 			short_channel_id:  0 , 
272- 			payment_relay:  PaymentRelay  { 
273- 				cltv_expiry_delta:  144 , 
274- 				fee_proportional_millionths:  500 , 
275- 				fee_base_msat:  100 , 
276- 			} , 
277- 			payment_constraints:  PaymentConstraints  { 
278- 				max_cltv_expiry:  0 , 
279- 				htlc_minimum_msat:  100 , 
281+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
282+ 			node_id:  dummy_pk, 
283+ 			tlvs:  ForwardTlvs  { 
284+ 				short_channel_id:  0 , 
285+ 				payment_relay:  PaymentRelay  { 
286+ 					cltv_expiry_delta:  144 , 
287+ 					fee_proportional_millionths:  500 , 
288+ 					fee_base_msat:  100 , 
289+ 				} , 
290+ 				payment_constraints:  PaymentConstraints  { 
291+ 					max_cltv_expiry:  0 , 
292+ 					htlc_minimum_msat:  100 , 
293+ 				} , 
294+ 				features:  BlindedHopFeatures :: empty( ) , 
280295			} , 
281- 			features:  BlindedHopFeatures :: empty( ) , 
282- 		} ,  u64 :: max_value( ) ) ,  ( dummy_pk,  ForwardTlvs  { 
283- 			short_channel_id:  0 , 
284- 			payment_relay:  PaymentRelay  { 
285- 				cltv_expiry_delta:  144 , 
286- 				fee_proportional_millionths:  500 , 
287- 				fee_base_msat:  100 , 
296+ 			htlc_maximum_msat:  u64 :: max_value( ) , 
297+ 		} ,  ForwardNode  { 
298+ 			node_id:  dummy_pk, 
299+ 			tlvs:  ForwardTlvs  { 
300+ 				short_channel_id:  0 , 
301+ 				payment_relay:  PaymentRelay  { 
302+ 					cltv_expiry_delta:  144 , 
303+ 					fee_proportional_millionths:  500 , 
304+ 					fee_base_msat:  100 , 
305+ 				} , 
306+ 				payment_constraints:  PaymentConstraints  { 
307+ 					max_cltv_expiry:  0 , 
308+ 					htlc_minimum_msat:  1_000 , 
309+ 				} , 
310+ 				features:  BlindedHopFeatures :: empty( ) , 
288311			} , 
289- 			payment_constraints:  PaymentConstraints  { 
290- 				max_cltv_expiry:  0 , 
291- 				htlc_minimum_msat:  1_000 , 
292- 			} , 
293- 			features:  BlindedHopFeatures :: empty( ) , 
294- 		} ,  u64 :: max_value( ) ) ] ; 
312+ 			htlc_maximum_msat:  u64 :: max_value( ) , 
313+ 		} ] ; 
295314		let  recv_tlvs = ReceiveTlvs  { 
296315			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
297316			payment_constraints :  PaymentConstraints  { 
@@ -330,31 +349,39 @@ mod tests {
330349		// If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat 
331350		// along the path. 
332351		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
333- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
334- 			short_channel_id:  0 , 
335- 			payment_relay:  PaymentRelay  { 
336- 				cltv_expiry_delta:  0 , 
337- 				fee_proportional_millionths:  0 , 
338- 				fee_base_msat:  0 , 
352+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
353+ 			node_id:  dummy_pk, 
354+ 			tlvs:  ForwardTlvs  { 
355+ 				short_channel_id:  0 , 
356+ 				payment_relay:  PaymentRelay  { 
357+ 					cltv_expiry_delta:  0 , 
358+ 					fee_proportional_millionths:  0 , 
359+ 					fee_base_msat:  0 , 
360+ 				} , 
361+ 				payment_constraints:  PaymentConstraints  { 
362+ 					max_cltv_expiry:  0 , 
363+ 					htlc_minimum_msat:  1 , 
364+ 				} , 
365+ 				features:  BlindedHopFeatures :: empty( ) , 
339366			} , 
340- 			payment_constraints :   PaymentConstraints   { 
341- 				max_cltv_expiry :   0 , 
342- 				htlc_minimum_msat :   1 , 
343- 			} , 
344- 			features :   BlindedHopFeatures :: empty ( ) , 
345- 		} ,   u64 :: max_value ( ) ) ,   ( dummy_pk ,   ForwardTlvs  { 
346- 			short_channel_id :  0 , 
347- 			payment_relay :   PaymentRelay   { 
348- 				cltv_expiry_delta :  0 , 
349- 				fee_proportional_millionths :   0 , 
350- 				fee_base_msat :   0 , 
351- 			} , 
352- 			payment_constraints :   PaymentConstraints   { 
353- 				max_cltv_expiry :   0 , 
354- 				htlc_minimum_msat :   2_000 , 
367+ 			htlc_maximum_msat :   u64 :: max_value ( ) 
368+ 		} ,   ForwardNode   { 
369+ 			node_id :  dummy_pk , 
370+ 			tlvs :   ForwardTlvs   { 
371+ 				short_channel_id :   0 , 
372+ 				payment_relay :   PaymentRelay  { 
373+ 					cltv_expiry_delta :  0 , 
374+ 					fee_proportional_millionths :   0 , 
375+ 					fee_base_msat :  0 , 
376+ 				} , 
377+ 				payment_constraints :   PaymentConstraints   { 
378+ 					max_cltv_expiry :   0 , 
379+ 					htlc_minimum_msat :   2_000 , 
380+ 				} , 
381+ 				features :   BlindedHopFeatures :: empty ( ) , 
355382			} , 
356- 			features :   BlindedHopFeatures :: empty ( ) , 
357- 		} ,   u64 :: max_value ( ) ) ] ; 
383+ 			htlc_maximum_msat :   u64 :: max_value ( ) 
384+ 		} ] ; 
358385		let  recv_tlvs = ReceiveTlvs  { 
359386			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
360387			payment_constraints :  PaymentConstraints  { 
@@ -372,31 +399,39 @@ mod tests {
372399		// Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the 
373400		// max (htlc_min - following_fees) along the path. 
374401		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
375- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
376- 			short_channel_id:  0 , 
377- 			payment_relay:  PaymentRelay  { 
378- 				cltv_expiry_delta:  0 , 
379- 				fee_proportional_millionths:  500 , 
380- 				fee_base_msat:  1_000 , 
381- 			} , 
382- 			payment_constraints:  PaymentConstraints  { 
383- 				max_cltv_expiry:  0 , 
384- 				htlc_minimum_msat:  5_000 , 
402+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
403+ 			node_id:  dummy_pk, 
404+ 			tlvs:  ForwardTlvs  { 
405+ 				short_channel_id:  0 , 
406+ 				payment_relay:  PaymentRelay  { 
407+ 					cltv_expiry_delta:  0 , 
408+ 					fee_proportional_millionths:  500 , 
409+ 					fee_base_msat:  1_000 , 
410+ 				} , 
411+ 				payment_constraints:  PaymentConstraints  { 
412+ 					max_cltv_expiry:  0 , 
413+ 					htlc_minimum_msat:  5_000 , 
414+ 				} , 
415+ 				features:  BlindedHopFeatures :: empty( ) , 
385416			} , 
386- 			features:  BlindedHopFeatures :: empty( ) , 
387- 		} ,  u64 :: max_value( ) ) ,  ( dummy_pk,  ForwardTlvs  { 
388- 			short_channel_id:  0 , 
389- 			payment_relay:  PaymentRelay  { 
390- 				cltv_expiry_delta:  0 , 
391- 				fee_proportional_millionths:  500 , 
392- 				fee_base_msat:  200 , 
417+ 			htlc_maximum_msat:  u64 :: max_value( ) 
418+ 		} ,  ForwardNode  { 
419+ 			node_id:  dummy_pk, 
420+ 			tlvs:  ForwardTlvs  { 
421+ 				short_channel_id:  0 , 
422+ 				payment_relay:  PaymentRelay  { 
423+ 					cltv_expiry_delta:  0 , 
424+ 					fee_proportional_millionths:  500 , 
425+ 					fee_base_msat:  200 , 
426+ 				} , 
427+ 				payment_constraints:  PaymentConstraints  { 
428+ 					max_cltv_expiry:  0 , 
429+ 					htlc_minimum_msat:  2_000 , 
430+ 				} , 
431+ 				features:  BlindedHopFeatures :: empty( ) , 
393432			} , 
394- 			payment_constraints:  PaymentConstraints  { 
395- 				max_cltv_expiry:  0 , 
396- 				htlc_minimum_msat:  2_000 , 
397- 			} , 
398- 			features:  BlindedHopFeatures :: empty( ) , 
399- 		} ,  u64 :: max_value( ) ) ] ; 
433+ 			htlc_maximum_msat:  u64 :: max_value( ) 
434+ 		} ] ; 
400435		let  recv_tlvs = ReceiveTlvs  { 
401436			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
402437			payment_constraints :  PaymentConstraints  { 
@@ -418,31 +453,39 @@ mod tests {
418453		// Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max 
419454		// htlc ends up as the min (htlc_max - following_fees) along the path. 
420455		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
421- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
422- 			short_channel_id:  0 , 
423- 			payment_relay:  PaymentRelay  { 
424- 				cltv_expiry_delta:  0 , 
425- 				fee_proportional_millionths:  500 , 
426- 				fee_base_msat:  1_000 , 
456+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
457+ 			node_id:  dummy_pk, 
458+ 			tlvs:  ForwardTlvs  { 
459+ 				short_channel_id:  0 , 
460+ 				payment_relay:  PaymentRelay  { 
461+ 					cltv_expiry_delta:  0 , 
462+ 					fee_proportional_millionths:  500 , 
463+ 					fee_base_msat:  1_000 , 
464+ 				} , 
465+ 				payment_constraints:  PaymentConstraints  { 
466+ 					max_cltv_expiry:  0 , 
467+ 					htlc_minimum_msat:  1 , 
468+ 				} , 
469+ 				features:  BlindedHopFeatures :: empty( ) , 
427470			} , 
428- 			payment_constraints :   PaymentConstraints   { 
429- 				max_cltv_expiry :   0 , 
430- 				htlc_minimum_msat :   1 , 
431- 			} , 
432- 			features :   BlindedHopFeatures :: empty ( ) , 
433- 		} ,   5_000 ) ,   ( dummy_pk ,   ForwardTlvs  { 
434- 			short_channel_id :  0 , 
435- 			payment_relay :   PaymentRelay   { 
436- 				cltv_expiry_delta :   0 , 
437- 				fee_proportional_millionths :   500 , 
438- 				fee_base_msat :   1 , 
439- 			} , 
440- 			payment_constraints :   PaymentConstraints   { 
441- 				max_cltv_expiry :   0 , 
442- 				htlc_minimum_msat :   1 , 
471+ 			htlc_maximum_msat :   5_000 , 
472+ 		} ,   ForwardNode   { 
473+ 			node_id :  dummy_pk , 
474+ 			tlvs :   ForwardTlvs   { 
475+ 				short_channel_id :   0 , 
476+ 				payment_relay :   PaymentRelay  { 
477+ 					cltv_expiry_delta :  0 , 
478+ 					fee_proportional_millionths :   500 , 
479+ 					fee_base_msat :   1 , 
480+ 				} , 
481+ 				payment_constraints :   PaymentConstraints   { 
482+ 					max_cltv_expiry :   0 , 
483+ 					htlc_minimum_msat :   1 , 
484+ 				} , 
485+ 				features :   BlindedHopFeatures :: empty ( ) , 
443486			} , 
444- 			features :   BlindedHopFeatures :: empty ( ) , 
445- 		} ,   10_000 ) ] ; 
487+ 			htlc_maximum_msat :   10_000 
488+ 		} ] ; 
446489		let  recv_tlvs = ReceiveTlvs  { 
447490			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
448491			payment_constraints :  PaymentConstraints  { 
0 commit comments