@@ -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 ( ( ) )  } 
@@ -215,16 +226,16 @@ pub(super) fn compute_payinfo(
215226
216227	let  mut  htlc_minimum_msat:  u64  = 1 ; 
217228	let  mut  htlc_maximum_msat:  u64  = 21_000_000  *  100_000_000  *  1_000 ;  // Total bitcoin supply 
218- 	for  ( _ ,  tlvs ,  max_htlc_candidate )  in  intermediate_nodes. iter ( )  { 
229+ 	for  node  in  intermediate_nodes. iter ( )  { 
219230		// The min htlc for an intermediate node is that node's min minus the fees charged by all of the 
220231		// following hops for forwarding that min, since that fee amount will automatically be included 
221232		// in the amount that this node receives and contribute towards reaching its min. 
222233		htlc_minimum_msat = amt_to_forward_msat ( 
223- 			core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat ,  htlc_minimum_msat) , 
224- 			& tlvs. payment_relay 
234+ 			core:: cmp:: max ( node . tlvs . payment_constraints . htlc_minimum_msat ,  htlc_minimum_msat) , 
235+ 			& node . tlvs . payment_relay 
225236		) . unwrap_or ( 1 ) ;  // If underflow occurs, we definitely reached this node's min 
226237		htlc_maximum_msat = amt_to_forward_msat ( 
227- 			core:: cmp:: min ( * max_htlc_candidate ,  htlc_maximum_msat) ,  & tlvs. payment_relay 
238+ 			core:: cmp:: min ( node . htlc_maximum_msat ,  htlc_maximum_msat) ,  & node . tlvs . payment_relay 
228239		) . ok_or ( ( ) ) ?;  // If underflow occurs, we cannot send to this hop without exceeding their max 
229240	} 
230241	htlc_minimum_msat = core:: cmp:: max ( 
@@ -257,7 +268,7 @@ impl_writeable_msg!(PaymentConstraints, {
257268#[ cfg( test) ]  
258269mod  tests { 
259270	use  bitcoin:: secp256k1:: PublicKey ; 
260- 	use  crate :: blinded_path:: payment:: { ForwardTlvs ,  ReceiveTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
271+ 	use  crate :: blinded_path:: payment:: { ForwardNode ,   ForwardTlvs ,  ReceiveTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
261272	use  crate :: ln:: PaymentSecret ; 
262273	use  crate :: ln:: features:: BlindedHopFeatures ; 
263274
@@ -266,31 +277,39 @@ mod tests {
266277		// Taken from the spec example for aggregating blinded payment info. See 
267278		// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md#blinded-payments 
268279		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
269- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
270- 			short_channel_id:  0 , 
271- 			payment_relay:  PaymentRelay  { 
272- 				cltv_expiry_delta:  144 , 
273- 				fee_proportional_millionths:  500 , 
274- 				fee_base_msat:  100 , 
275- 			} , 
276- 			payment_constraints:  PaymentConstraints  { 
277- 				max_cltv_expiry:  0 , 
278- 				htlc_minimum_msat:  100 , 
280+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
281+ 			node_id:  dummy_pk, 
282+ 			tlvs:  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 , 
288+ 				} , 
289+ 				payment_constraints:  PaymentConstraints  { 
290+ 					max_cltv_expiry:  0 , 
291+ 					htlc_minimum_msat:  100 , 
292+ 				} , 
293+ 				features:  BlindedHopFeatures :: empty( ) , 
279294			} , 
280- 			features:  BlindedHopFeatures :: empty( ) , 
281- 		} ,  u64 :: max_value( ) ) ,  ( dummy_pk,  ForwardTlvs  { 
282- 			short_channel_id:  0 , 
283- 			payment_relay:  PaymentRelay  { 
284- 				cltv_expiry_delta:  144 , 
285- 				fee_proportional_millionths:  500 , 
286- 				fee_base_msat:  100 , 
295+ 			htlc_maximum_msat:  u64 :: max_value( ) , 
296+ 		} ,  ForwardNode  { 
297+ 			node_id:  dummy_pk, 
298+ 			tlvs:  ForwardTlvs  { 
299+ 				short_channel_id:  0 , 
300+ 				payment_relay:  PaymentRelay  { 
301+ 					cltv_expiry_delta:  144 , 
302+ 					fee_proportional_millionths:  500 , 
303+ 					fee_base_msat:  100 , 
304+ 				} , 
305+ 				payment_constraints:  PaymentConstraints  { 
306+ 					max_cltv_expiry:  0 , 
307+ 					htlc_minimum_msat:  1_000 , 
308+ 				} , 
309+ 				features:  BlindedHopFeatures :: empty( ) , 
287310			} , 
288- 			payment_constraints:  PaymentConstraints  { 
289- 				max_cltv_expiry:  0 , 
290- 				htlc_minimum_msat:  1_000 , 
291- 			} , 
292- 			features:  BlindedHopFeatures :: empty( ) , 
293- 		} ,  u64 :: max_value( ) ) ] ; 
311+ 			htlc_maximum_msat:  u64 :: max_value( ) , 
312+ 		} ] ; 
294313		let  recv_tlvs = ReceiveTlvs  { 
295314			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
296315			payment_constraints :  PaymentConstraints  { 
@@ -329,31 +348,39 @@ mod tests {
329348		// If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat 
330349		// along the path. 
331350		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
332- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
333- 			short_channel_id:  0 , 
334- 			payment_relay:  PaymentRelay  { 
335- 				cltv_expiry_delta:  0 , 
336- 				fee_proportional_millionths:  0 , 
337- 				fee_base_msat:  0 , 
351+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
352+ 			node_id:  dummy_pk, 
353+ 			tlvs:  ForwardTlvs  { 
354+ 				short_channel_id:  0 , 
355+ 				payment_relay:  PaymentRelay  { 
356+ 					cltv_expiry_delta:  0 , 
357+ 					fee_proportional_millionths:  0 , 
358+ 					fee_base_msat:  0 , 
359+ 				} , 
360+ 				payment_constraints:  PaymentConstraints  { 
361+ 					max_cltv_expiry:  0 , 
362+ 					htlc_minimum_msat:  1 , 
363+ 				} , 
364+ 				features:  BlindedHopFeatures :: empty( ) , 
338365			} , 
339- 			payment_constraints :   PaymentConstraints   { 
340- 				max_cltv_expiry :   0 , 
341- 				htlc_minimum_msat :   1 , 
342- 			} , 
343- 			features :   BlindedHopFeatures :: empty ( ) , 
344- 		} ,   u64 :: max_value ( ) ) ,   ( dummy_pk ,   ForwardTlvs  { 
345- 			short_channel_id :  0 , 
346- 			payment_relay :   PaymentRelay   { 
347- 				cltv_expiry_delta :  0 , 
348- 				fee_proportional_millionths :   0 , 
349- 				fee_base_msat :   0 , 
350- 			} , 
351- 			payment_constraints :   PaymentConstraints   { 
352- 				max_cltv_expiry :   0 , 
353- 				htlc_minimum_msat :   2_000 , 
366+ 			htlc_maximum_msat :   u64 :: max_value ( ) 
367+ 		} ,   ForwardNode   { 
368+ 			node_id :  dummy_pk , 
369+ 			tlvs :   ForwardTlvs   { 
370+ 				short_channel_id :   0 , 
371+ 				payment_relay :   PaymentRelay  { 
372+ 					cltv_expiry_delta :  0 , 
373+ 					fee_proportional_millionths :   0 , 
374+ 					fee_base_msat :  0 , 
375+ 				} , 
376+ 				payment_constraints :   PaymentConstraints   { 
377+ 					max_cltv_expiry :   0 , 
378+ 					htlc_minimum_msat :   2_000 , 
379+ 				} , 
380+ 				features :   BlindedHopFeatures :: empty ( ) , 
354381			} , 
355- 			features :   BlindedHopFeatures :: empty ( ) , 
356- 		} ,   u64 :: max_value ( ) ) ] ; 
382+ 			htlc_maximum_msat :   u64 :: max_value ( ) 
383+ 		} ] ; 
357384		let  recv_tlvs = ReceiveTlvs  { 
358385			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
359386			payment_constraints :  PaymentConstraints  { 
@@ -371,31 +398,39 @@ mod tests {
371398		// Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the 
372399		// max (htlc_min - following_fees) along the path. 
373400		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
374- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
375- 			short_channel_id:  0 , 
376- 			payment_relay:  PaymentRelay  { 
377- 				cltv_expiry_delta:  0 , 
378- 				fee_proportional_millionths:  500 , 
379- 				fee_base_msat:  1_000 , 
380- 			} , 
381- 			payment_constraints:  PaymentConstraints  { 
382- 				max_cltv_expiry:  0 , 
383- 				htlc_minimum_msat:  5_000 , 
401+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
402+ 			node_id:  dummy_pk, 
403+ 			tlvs:  ForwardTlvs  { 
404+ 				short_channel_id:  0 , 
405+ 				payment_relay:  PaymentRelay  { 
406+ 					cltv_expiry_delta:  0 , 
407+ 					fee_proportional_millionths:  500 , 
408+ 					fee_base_msat:  1_000 , 
409+ 				} , 
410+ 				payment_constraints:  PaymentConstraints  { 
411+ 					max_cltv_expiry:  0 , 
412+ 					htlc_minimum_msat:  5_000 , 
413+ 				} , 
414+ 				features:  BlindedHopFeatures :: empty( ) , 
384415			} , 
385- 			features:  BlindedHopFeatures :: empty( ) , 
386- 		} ,  u64 :: max_value( ) ) ,  ( dummy_pk,  ForwardTlvs  { 
387- 			short_channel_id:  0 , 
388- 			payment_relay:  PaymentRelay  { 
389- 				cltv_expiry_delta:  0 , 
390- 				fee_proportional_millionths:  500 , 
391- 				fee_base_msat:  200 , 
416+ 			htlc_maximum_msat:  u64 :: max_value( ) 
417+ 		} ,  ForwardNode  { 
418+ 			node_id:  dummy_pk, 
419+ 			tlvs:  ForwardTlvs  { 
420+ 				short_channel_id:  0 , 
421+ 				payment_relay:  PaymentRelay  { 
422+ 					cltv_expiry_delta:  0 , 
423+ 					fee_proportional_millionths:  500 , 
424+ 					fee_base_msat:  200 , 
425+ 				} , 
426+ 				payment_constraints:  PaymentConstraints  { 
427+ 					max_cltv_expiry:  0 , 
428+ 					htlc_minimum_msat:  2_000 , 
429+ 				} , 
430+ 				features:  BlindedHopFeatures :: empty( ) , 
392431			} , 
393- 			payment_constraints:  PaymentConstraints  { 
394- 				max_cltv_expiry:  0 , 
395- 				htlc_minimum_msat:  2_000 , 
396- 			} , 
397- 			features:  BlindedHopFeatures :: empty( ) , 
398- 		} ,  u64 :: max_value( ) ) ] ; 
432+ 			htlc_maximum_msat:  u64 :: max_value( ) 
433+ 		} ] ; 
399434		let  recv_tlvs = ReceiveTlvs  { 
400435			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
401436			payment_constraints :  PaymentConstraints  { 
@@ -417,31 +452,39 @@ mod tests {
417452		// Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max 
418453		// htlc ends up as the min (htlc_max - following_fees) along the path. 
419454		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
420- 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
421- 			short_channel_id:  0 , 
422- 			payment_relay:  PaymentRelay  { 
423- 				cltv_expiry_delta:  0 , 
424- 				fee_proportional_millionths:  500 , 
425- 				fee_base_msat:  1_000 , 
455+ 		let  intermediate_nodes = vec ! [ ForwardNode  { 
456+ 			node_id:  dummy_pk, 
457+ 			tlvs:  ForwardTlvs  { 
458+ 				short_channel_id:  0 , 
459+ 				payment_relay:  PaymentRelay  { 
460+ 					cltv_expiry_delta:  0 , 
461+ 					fee_proportional_millionths:  500 , 
462+ 					fee_base_msat:  1_000 , 
463+ 				} , 
464+ 				payment_constraints:  PaymentConstraints  { 
465+ 					max_cltv_expiry:  0 , 
466+ 					htlc_minimum_msat:  1 , 
467+ 				} , 
468+ 				features:  BlindedHopFeatures :: empty( ) , 
426469			} , 
427- 			payment_constraints :   PaymentConstraints   { 
428- 				max_cltv_expiry :   0 , 
429- 				htlc_minimum_msat :   1 , 
430- 			} , 
431- 			features :   BlindedHopFeatures :: empty ( ) , 
432- 		} ,   5_000 ) ,   ( dummy_pk ,   ForwardTlvs  { 
433- 			short_channel_id :  0 , 
434- 			payment_relay :   PaymentRelay   { 
435- 				cltv_expiry_delta :   0 , 
436- 				fee_proportional_millionths :   500 , 
437- 				fee_base_msat :   1 , 
438- 			} , 
439- 			payment_constraints :   PaymentConstraints   { 
440- 				max_cltv_expiry :   0 , 
441- 				htlc_minimum_msat :   1 , 
470+ 			htlc_maximum_msat :   5_000 , 
471+ 		} ,   ForwardNode   { 
472+ 			node_id :  dummy_pk , 
473+ 			tlvs :   ForwardTlvs   { 
474+ 				short_channel_id :   0 , 
475+ 				payment_relay :   PaymentRelay  { 
476+ 					cltv_expiry_delta :  0 , 
477+ 					fee_proportional_millionths :   500 , 
478+ 					fee_base_msat :   1 , 
479+ 				} , 
480+ 				payment_constraints :   PaymentConstraints   { 
481+ 					max_cltv_expiry :   0 , 
482+ 					htlc_minimum_msat :   1 , 
483+ 				} , 
484+ 				features :   BlindedHopFeatures :: empty ( ) , 
442485			} , 
443- 			features :   BlindedHopFeatures :: empty ( ) , 
444- 		} ,   10_000 ) ] ; 
486+ 			htlc_maximum_msat :   10_000 
487+ 		} ] ; 
445488		let  recv_tlvs = ReceiveTlvs  { 
446489			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
447490			payment_constraints :  PaymentConstraints  { 
0 commit comments