@@ -10,9 +10,12 @@ use crate::io;
1010use  crate :: ln:: PaymentSecret ; 
1111use  crate :: ln:: features:: BlindedHopFeatures ; 
1212use  crate :: ln:: msgs:: DecodeError ; 
13+ use  crate :: offers:: invoice:: BlindedPayInfo ; 
1314use  crate :: prelude:: * ; 
1415use  crate :: util:: ser:: { Readable ,  Writeable ,  Writer } ; 
1516
17+ use  core:: convert:: TryFrom ; 
18+ 
1619/// Data to construct a [`BlindedHop`] for forwarding a payment. 
1720pub  struct  ForwardTlvs  { 
1821	/// The short channel id this payment should be forwarded out over. 
@@ -150,6 +153,47 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
150153	utils:: construct_blinded_hops ( secp_ctx,  pks,  tlvs,  session_priv) 
151154} 
152155
156+ pub ( super )  fn  compute_payinfo ( 
157+ 	intermediate_nodes :  & [ ( PublicKey ,  ForwardTlvs ) ] ,  payee_tlvs :  & ReceiveTlvs 
158+ )  -> Result < BlindedPayInfo ,  ( ) >  { 
159+ 	let  mut  curr_base_fee:  u128  = 0 ; 
160+ 	let  mut  curr_prop_mil:  u128  = 0 ; 
161+ 	let  mut  cltv_expiry_delta:  u16  = 0 ; 
162+ 	for  ( _,  tlvs)  in  intermediate_nodes. iter ( ) . rev ( )  { 
163+ 		// In the future, we'll want to take the intersection of all supported features for the 
164+ 		// `BlindedPayInfo`, but there are no features in that context right now. 
165+ 		if  tlvs. features . requires_unknown_bits ( )  {  return  Err ( ( ) )  } 
166+ 
167+ 		let  next_base_fee = tlvs. payment_relay . fee_base_msat  as  u128 ; 
168+ 		let  next_prop_mil = tlvs. payment_relay . fee_proportional_millionths  as  u128 ; 
169+ 		// Use integer arithmetic to compute `ceil(a/b)` as `(a+b-1)/b` 
170+ 		// ((next_base_fee * 1_000_000 + (curr_base_fee * (1_000_000 + next_prop_mil))) + 1_000_000 - 1) / 1_000_000 
171+ 		curr_base_fee = next_prop_mil. checked_add ( 1_000_000 ) 
172+ 			. and_then ( |f| f. checked_mul ( curr_base_fee) ) 
173+ 			. and_then ( |f| next_base_fee. checked_mul ( 1_000_000 ) . and_then ( |base| base. checked_add ( f) ) ) 
174+ 			. and_then ( |f| f. checked_add ( 1_000_000  - 1 ) ) 
175+ 			. map ( |f| f / 1_000_000 ) 
176+ 			. ok_or ( ( ) ) ?; 
177+ 		// (((curr_prop_mil + next_prop_mil) * 1_000_000 + curr_prop_mil * next_prop_mil) + 1_000_000 - 1) / 1_000_000 
178+ 		curr_prop_mil = curr_prop_mil. checked_add ( next_prop_mil) 
179+ 			. and_then ( |f| f. checked_mul ( 1_000_000 ) ) 
180+ 			. and_then ( |f| curr_prop_mil. checked_mul ( next_prop_mil) . and_then ( |prop_mil| prop_mil. checked_add ( f) ) ) 
181+ 			. and_then ( |f| f. checked_add ( 1_000_000  - 1 ) ) 
182+ 			. map ( |f| f / 1_000_000 ) 
183+ 			. ok_or ( ( ) ) ?; 
184+ 
185+ 		cltv_expiry_delta = cltv_expiry_delta. checked_add ( tlvs. payment_relay . cltv_expiry_delta ) . ok_or ( ( ) ) ?; 
186+ 	} 
187+ 	Ok ( BlindedPayInfo  { 
188+ 		fee_base_msat :  u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?, 
189+ 		fee_proportional_millionths :  u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?, 
190+ 		cltv_expiry_delta, 
191+ 		htlc_minimum_msat :  1 ,  // TODO 
192+ 		htlc_maximum_msat :  21_000_000  *  100_000_000  *  1_000 ,  // TODO 
193+ 		features :  BlindedHopFeatures :: empty ( ) , 
194+ 	} ) 
195+ } 
196+ 
153197impl_writeable_msg ! ( PaymentRelay ,  { 
154198	cltv_expiry_delta, 
155199	fee_proportional_millionths, 
@@ -160,3 +204,69 @@ impl_writeable_msg!(PaymentConstraints, {
160204	max_cltv_expiry, 
161205	htlc_minimum_msat
162206} ,  { } ) ; 
207+ 
208+ #[ cfg( test) ]  
209+ mod  tests { 
210+ 	use  bitcoin:: secp256k1:: PublicKey ; 
211+ 	use  crate :: blinded_path:: payment:: { ForwardTlvs ,  ReceiveTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
212+ 	use  crate :: ln:: PaymentSecret ; 
213+ 	use  crate :: ln:: features:: BlindedHopFeatures ; 
214+ 
215+ 	#[ test]  
216+ 	fn  compute_payinfo ( )  { 
217+ 		// Taken from the spec example for aggregating blinded payment info. See 
218+ 		// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md#blinded-payments 
219+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
220+ 		let  intermediate_nodes = vec ! [ ( dummy_pk,  ForwardTlvs  { 
221+ 			short_channel_id:  0 , 
222+ 			payment_relay:  PaymentRelay  { 
223+ 				cltv_expiry_delta:  144 , 
224+ 				fee_proportional_millionths:  500 , 
225+ 				fee_base_msat:  100 , 
226+ 			} , 
227+ 			payment_constraints:  PaymentConstraints  { 
228+ 				max_cltv_expiry:  0 , 
229+ 				htlc_minimum_msat:  100 , 
230+ 			} , 
231+ 			features:  BlindedHopFeatures :: empty( ) , 
232+ 		} ) ,  ( dummy_pk,  ForwardTlvs  { 
233+ 			short_channel_id:  0 , 
234+ 			payment_relay:  PaymentRelay  { 
235+ 				cltv_expiry_delta:  144 , 
236+ 				fee_proportional_millionths:  500 , 
237+ 				fee_base_msat:  100 , 
238+ 			} , 
239+ 			payment_constraints:  PaymentConstraints  { 
240+ 				max_cltv_expiry:  0 , 
241+ 				htlc_minimum_msat:  1_000 , 
242+ 			} , 
243+ 			features:  BlindedHopFeatures :: empty( ) , 
244+ 		} ) ] ; 
245+ 		let  recv_tlvs = ReceiveTlvs  { 
246+ 			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
247+ 			payment_constraints :  PaymentConstraints  { 
248+ 				max_cltv_expiry :  0 , 
249+ 				htlc_minimum_msat :  1 , 
250+ 			} , 
251+ 		} ; 
252+ 		let  blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] ,  & recv_tlvs) . unwrap ( ) ; 
253+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  201 ) ; 
254+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  1001 ) ; 
255+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  288 ) ; 
256+ 	} 
257+ 
258+ 	#[ test]  
259+ 	fn  compute_payinfo_1_hop ( )  { 
260+ 		let  recv_tlvs = ReceiveTlvs  { 
261+ 			payment_secret :  PaymentSecret ( [ 0 ;  32 ] ) , 
262+ 			payment_constraints :  PaymentConstraints  { 
263+ 				max_cltv_expiry :  0 , 
264+ 				htlc_minimum_msat :  1 , 
265+ 			} , 
266+ 		} ; 
267+ 		let  blinded_payinfo = super :: compute_payinfo ( & [ ] ,  & recv_tlvs) . unwrap ( ) ; 
268+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  0 ) ; 
269+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  0 ) ; 
270+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  0 ) ; 
271+ 	} 
272+ } 
0 commit comments